Path: blob/main/sys/contrib/dev/athk/ath11k/debugfs.c
105419 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.3* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.4* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.5*/67#include <linux/export.h>8#include <linux/vmalloc.h>910#include "debugfs.h"1112#include "core.h"13#include "debug.h"14#include "wmi.h"15#include "hal_rx.h"16#include "dp_tx.h"17#include "debugfs_htt_stats.h"18#include "peer.h"19#include "hif.h"2021static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {22"REO2SW1_RING",23"REO2SW2_RING",24"REO2SW3_RING",25"REO2SW4_RING",26"WBM2REO_LINK_RING",27"REO2TCL_RING",28"REO2FW_RING",29"RELEASE_RING",30"PPE_RELEASE_RING",31"TCL2TQM_RING",32"TQM_RELEASE_RING",33"REO_RELEASE_RING",34"WBM2SW0_RELEASE_RING",35"WBM2SW1_RELEASE_RING",36"WBM2SW2_RELEASE_RING",37"WBM2SW3_RELEASE_RING",38"REO_CMD_RING",39"REO_STATUS_RING",40};4142static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {43"FW2RXDMA_BUF_RING",44"FW2RXDMA_STATUS_RING",45"FW2RXDMA_LINK_RING",46"SW2RXDMA_BUF_RING",47"WBM2RXDMA_LINK_RING",48"RXDMA2FW_RING",49"RXDMA2SW_RING",50"RXDMA2RELEASE_RING",51"RXDMA2REO_RING",52"MONITOR_STATUS_RING",53"MONITOR_BUF_RING",54"MONITOR_DESC_RING",55"MONITOR_DEST_RING",56};5758void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,59enum wmi_direct_buffer_module id,60enum ath11k_dbg_dbr_event event,61struct hal_srng *srng)62{63struct ath11k_debug_dbr *dbr_debug;64struct ath11k_dbg_dbr_data *dbr_data;65struct ath11k_dbg_dbr_entry *entry;6667if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX)68return;6970dbr_debug = ar->debug.dbr_debug[id];71if (!dbr_debug)72return;7374if (!dbr_debug->dbr_debug_enabled)75return;7677dbr_data = &dbr_debug->dbr_dbg_data;7879spin_lock_bh(&dbr_data->lock);8081if (dbr_data->entries) {82entry = &dbr_data->entries[dbr_data->dbr_debug_idx];83entry->hp = srng->u.src_ring.hp;84entry->tp = *srng->u.src_ring.tp_addr;85entry->timestamp = jiffies;86entry->event = event;8788dbr_data->dbr_debug_idx++;89if (dbr_data->dbr_debug_idx ==90dbr_data->num_ring_debug_entries)91dbr_data->dbr_debug_idx = 0;92}9394spin_unlock_bh(&dbr_data->lock);95}9697void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats)98{99struct ath11k_base *ab = ar->ab;100bool is_end = true;101102/* WMI_REQUEST_PDEV_STAT, WMI_REQUEST_RSSI_PER_CHAIN_STAT and103* WMI_REQUEST_VDEV_STAT requests have been already processed.104*/105if (stats->stats_id == WMI_REQUEST_BCN_STAT) {106if (list_empty(&stats->bcn)) {107ath11k_warn(ab, "empty bcn stats");108return;109}110/* Mark end until we reached the count of all started VDEVs111* within the PDEV112*/113if (ar->num_started_vdevs)114is_end = ((++ar->fw_stats.num_bcn_recvd) ==115ar->num_started_vdevs);116117list_splice_tail_init(&stats->bcn,118&ar->fw_stats.bcn);119120if (is_end)121complete(&ar->fw_stats_done);122}123}124125static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)126{127struct ath11k *ar = inode->i_private;128struct ath11k_base *ab = ar->ab;129struct stats_request_params req_param;130void *buf = NULL;131int ret;132133mutex_lock(&ar->conf_mutex);134135if (ar->state != ATH11K_STATE_ON) {136ret = -ENETDOWN;137goto err_unlock;138}139140buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);141if (!buf) {142ret = -ENOMEM;143goto err_unlock;144}145146req_param.pdev_id = ar->pdev->pdev_id;147req_param.vdev_id = 0;148req_param.stats_id = WMI_REQUEST_PDEV_STAT;149150ret = ath11k_mac_fw_stats_request(ar, &req_param);151if (ret) {152ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);153goto err_free;154}155156ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);157158file->private_data = buf;159160mutex_unlock(&ar->conf_mutex);161return 0;162163err_free:164vfree(buf);165166err_unlock:167mutex_unlock(&ar->conf_mutex);168return ret;169}170171static int ath11k_release_pdev_stats(struct inode *inode, struct file *file)172{173vfree(file->private_data);174175return 0;176}177178static ssize_t ath11k_read_pdev_stats(struct file *file,179char __user *user_buf,180size_t count, loff_t *ppos)181{182const char *buf = file->private_data;183size_t len = strlen(buf);184185return simple_read_from_buffer(user_buf, count, ppos, buf, len);186}187188static const struct file_operations fops_pdev_stats = {189.open = ath11k_open_pdev_stats,190.release = ath11k_release_pdev_stats,191.read = ath11k_read_pdev_stats,192.owner = THIS_MODULE,193.llseek = default_llseek,194};195196static int ath11k_open_vdev_stats(struct inode *inode, struct file *file)197{198struct ath11k *ar = inode->i_private;199struct stats_request_params req_param;200void *buf = NULL;201int ret;202203mutex_lock(&ar->conf_mutex);204205if (ar->state != ATH11K_STATE_ON) {206ret = -ENETDOWN;207goto err_unlock;208}209210buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);211if (!buf) {212ret = -ENOMEM;213goto err_unlock;214}215216req_param.pdev_id = ar->pdev->pdev_id;217/* VDEV stats is always sent for all active VDEVs from FW */218req_param.vdev_id = 0;219req_param.stats_id = WMI_REQUEST_VDEV_STAT;220221ret = ath11k_mac_fw_stats_request(ar, &req_param);222if (ret) {223ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);224goto err_free;225}226227ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);228229file->private_data = buf;230231mutex_unlock(&ar->conf_mutex);232return 0;233234err_free:235vfree(buf);236237err_unlock:238mutex_unlock(&ar->conf_mutex);239return ret;240}241242static int ath11k_release_vdev_stats(struct inode *inode, struct file *file)243{244vfree(file->private_data);245246return 0;247}248249static ssize_t ath11k_read_vdev_stats(struct file *file,250char __user *user_buf,251size_t count, loff_t *ppos)252{253const char *buf = file->private_data;254size_t len = strlen(buf);255256return simple_read_from_buffer(user_buf, count, ppos, buf, len);257}258259static const struct file_operations fops_vdev_stats = {260.open = ath11k_open_vdev_stats,261.release = ath11k_release_vdev_stats,262.read = ath11k_read_vdev_stats,263.owner = THIS_MODULE,264.llseek = default_llseek,265};266267static int ath11k_open_bcn_stats(struct inode *inode, struct file *file)268{269struct ath11k *ar = inode->i_private;270struct ath11k_vif *arvif;271struct stats_request_params req_param;272void *buf = NULL;273int ret;274275mutex_lock(&ar->conf_mutex);276277if (ar->state != ATH11K_STATE_ON) {278ret = -ENETDOWN;279goto err_unlock;280}281282buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);283if (!buf) {284ret = -ENOMEM;285goto err_unlock;286}287288req_param.stats_id = WMI_REQUEST_BCN_STAT;289req_param.pdev_id = ar->pdev->pdev_id;290291/* loop all active VDEVs for bcn stats */292list_for_each_entry(arvif, &ar->arvifs, list) {293if (!arvif->is_up)294continue;295296req_param.vdev_id = arvif->vdev_id;297ret = ath11k_mac_fw_stats_request(ar, &req_param);298if (ret) {299ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);300goto err_free;301}302}303304ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);305306/* since beacon stats request is looped for all active VDEVs, saved fw307* stats is not freed for each request until done for all active VDEVs308*/309spin_lock_bh(&ar->data_lock);310ath11k_fw_stats_bcn_free(&ar->fw_stats.bcn);311spin_unlock_bh(&ar->data_lock);312313file->private_data = buf;314315mutex_unlock(&ar->conf_mutex);316return 0;317318err_free:319vfree(buf);320321err_unlock:322mutex_unlock(&ar->conf_mutex);323return ret;324}325326static int ath11k_release_bcn_stats(struct inode *inode, struct file *file)327{328vfree(file->private_data);329330return 0;331}332333static ssize_t ath11k_read_bcn_stats(struct file *file,334char __user *user_buf,335size_t count, loff_t *ppos)336{337const char *buf = file->private_data;338size_t len = strlen(buf);339340return simple_read_from_buffer(user_buf, count, ppos, buf, len);341}342343static const struct file_operations fops_bcn_stats = {344.open = ath11k_open_bcn_stats,345.release = ath11k_release_bcn_stats,346.read = ath11k_read_bcn_stats,347.owner = THIS_MODULE,348.llseek = default_llseek,349};350351static ssize_t ath11k_read_simulate_fw_crash(struct file *file,352char __user *user_buf,353size_t count, loff_t *ppos)354{355const char buf[] =356"To simulate firmware crash write one of the keywords to this file:\n"357"`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"358"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";359360return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));361}362363/* Simulate firmware crash:364* 'soft': Call wmi command causing firmware hang. This firmware hang is365* recoverable by warm firmware reset.366* 'hard': Force firmware crash by setting any vdev parameter for not allowed367* vdev id. This is hard firmware crash because it is recoverable only by cold368* firmware reset.369*/370static ssize_t ath11k_write_simulate_fw_crash(struct file *file,371const char __user *user_buf,372size_t count, loff_t *ppos)373{374struct ath11k_base *ab = file->private_data;375struct ath11k_pdev *pdev;376struct ath11k *ar = ab->pdevs[0].ar;377char buf[32] = {};378ssize_t rc;379int i, ret, radioup = 0;380381for (i = 0; i < ab->num_radios; i++) {382pdev = &ab->pdevs[i];383ar = pdev->ar;384if (ar && ar->state == ATH11K_STATE_ON) {385radioup = 1;386break;387}388}389/* filter partial writes and invalid commands */390if (*ppos != 0 || count >= sizeof(buf) || count == 0)391return -EINVAL;392393rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);394if (rc < 0)395return rc;396397/* drop the possible '\n' from the end */398if (buf[*ppos - 1] == '\n')399buf[*ppos - 1] = '\0';400401if (radioup == 0) {402ret = -ENETDOWN;403goto exit;404}405406if (!strcmp(buf, "assert")) {407ath11k_info(ab, "simulating firmware assert crash\n");408ret = ath11k_wmi_force_fw_hang_cmd(ar,409ATH11K_WMI_FW_HANG_ASSERT_TYPE,410ATH11K_WMI_FW_HANG_DELAY);411} else if (!strcmp(buf, "hw-restart")) {412ath11k_info(ab, "user requested hw restart\n");413queue_work(ab->workqueue_aux, &ab->reset_work);414ret = 0;415} else {416ret = -EINVAL;417goto exit;418}419420if (ret) {421ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret);422goto exit;423}424425ret = count;426427exit:428return ret;429}430431static const struct file_operations fops_simulate_fw_crash = {432.read = ath11k_read_simulate_fw_crash,433.write = ath11k_write_simulate_fw_crash,434.open = simple_open,435.owner = THIS_MODULE,436.llseek = default_llseek,437};438439static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file,440const char __user *ubuf,441size_t count, loff_t *ppos)442{443struct ath11k *ar = file->private_data;444u32 filter;445int ret;446447if (kstrtouint_from_user(ubuf, count, 0, &filter))448return -EINVAL;449450mutex_lock(&ar->conf_mutex);451452if (ar->state != ATH11K_STATE_ON) {453ret = -ENETDOWN;454goto out;455}456457if (filter == ar->debug.extd_tx_stats) {458ret = count;459goto out;460}461462ar->debug.extd_tx_stats = filter;463ret = count;464465out:466mutex_unlock(&ar->conf_mutex);467return ret;468}469470static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file,471char __user *ubuf,472size_t count, loff_t *ppos)473474{475char buf[32] = {};476struct ath11k *ar = file->private_data;477int len = 0;478479mutex_lock(&ar->conf_mutex);480len = scnprintf(buf, sizeof(buf) - len, "%08x\n",481ar->debug.extd_tx_stats);482mutex_unlock(&ar->conf_mutex);483484return simple_read_from_buffer(ubuf, count, ppos, buf, len);485}486487static const struct file_operations fops_extd_tx_stats = {488.read = ath11k_read_enable_extd_tx_stats,489.write = ath11k_write_enable_extd_tx_stats,490.open = simple_open491};492493static ssize_t ath11k_write_extd_rx_stats(struct file *file,494const char __user *ubuf,495size_t count, loff_t *ppos)496{497struct ath11k *ar = file->private_data;498struct ath11k_base *ab = ar->ab;499struct htt_rx_ring_tlv_filter tlv_filter = {};500u32 enable, rx_filter = 0, ring_id;501int i;502int ret;503504if (kstrtouint_from_user(ubuf, count, 0, &enable))505return -EINVAL;506507mutex_lock(&ar->conf_mutex);508509if (ar->state != ATH11K_STATE_ON) {510ret = -ENETDOWN;511goto exit;512}513514if (enable > 1) {515ret = -EINVAL;516goto exit;517}518519if (enable == ar->debug.extd_rx_stats) {520ret = count;521goto exit;522}523524if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) {525ar->debug.extd_rx_stats = enable;526ret = count;527goto exit;528}529530if (enable) {531rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START;532rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;533rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;534rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;535rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;536rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;537538tlv_filter.rx_filter = rx_filter;539tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;540tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;541tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;542tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |543HTT_RX_FP_DATA_FILTER_FLASG3;544} else {545tlv_filter = ath11k_mac_mon_status_filter_default;546}547548ar->debug.rx_filter = tlv_filter.rx_filter;549550for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {551ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;552ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,553HAL_RXDMA_MONITOR_STATUS,554DP_RX_BUFFER_SIZE, &tlv_filter);555556if (ret) {557ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");558goto exit;559}560}561562ar->debug.extd_rx_stats = enable;563ret = count;564exit:565mutex_unlock(&ar->conf_mutex);566return ret;567}568569static ssize_t ath11k_read_extd_rx_stats(struct file *file,570char __user *ubuf,571size_t count, loff_t *ppos)572{573struct ath11k *ar = file->private_data;574char buf[32];575int len = 0;576577mutex_lock(&ar->conf_mutex);578len = scnprintf(buf, sizeof(buf) - len, "%d\n",579ar->debug.extd_rx_stats);580mutex_unlock(&ar->conf_mutex);581582return simple_read_from_buffer(ubuf, count, ppos, buf, len);583}584585static const struct file_operations fops_extd_rx_stats = {586.read = ath11k_read_extd_rx_stats,587.write = ath11k_write_extd_rx_stats,588.open = simple_open,589};590591static int ath11k_fill_bp_stats(struct ath11k_base *ab,592struct ath11k_bp_stats *bp_stats,593char *buf, int len, int size)594{595lockdep_assert_held(&ab->base_lock);596597len += scnprintf(buf + len, size - len, "count: %u\n",598bp_stats->count);599len += scnprintf(buf + len, size - len, "hp: %u\n",600bp_stats->hp);601len += scnprintf(buf + len, size - len, "tp: %u\n",602bp_stats->tp);603len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",604jiffies_to_msecs(jiffies - bp_stats->jiffies));605return len;606}607608static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab,609char *buf, int size)610{611struct ath11k_bp_stats *bp_stats;612bool stats_rxd = false;613u8 i, pdev_idx;614int len = 0;615616len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");617len += scnprintf(buf + len, size - len, "==================\n");618619spin_lock_bh(&ab->base_lock);620for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {621bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];622623if (!bp_stats->count)624continue;625626len += scnprintf(buf + len, size - len, "Ring: %s\n",627htt_bp_umac_ring[i]);628len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);629stats_rxd = true;630}631632for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {633for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {634bp_stats =635&ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];636637if (!bp_stats->count)638continue;639640len += scnprintf(buf + len, size - len, "Ring: %s\n",641htt_bp_lmac_ring[i]);642len += scnprintf(buf + len, size - len, "pdev: %d\n",643pdev_idx);644len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);645stats_rxd = true;646}647}648spin_unlock_bh(&ab->base_lock);649650if (!stats_rxd)651len += scnprintf(buf + len, size - len,652"No Ring Backpressure stats received\n\n");653654return len;655}656657static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file,658char __user *user_buf,659size_t count, loff_t *ppos)660{661struct ath11k_base *ab = file->private_data;662struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;663int len = 0, i, retval;664const int size = 4096;665static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {666"Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC",667"Unencrypt", "MSDU len", "MSDU limit", "WiFi parse",668"AMSDU parse", "SA timeout", "DA timeout",669"Flow timeout", "Flush req"};670static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = {671"Desc addr zero", "Desc inval", "AMPDU in non BA",672"Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump",673"Frame OOR", "BAR OOR", "No BA session",674"Frame SN equal SSN", "PN check fail", "2k err",675"PN err", "Desc blocked"};676677char *buf;678679buf = kzalloc(size, GFP_KERNEL);680if (!buf)681return -ENOMEM;682683len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n");684len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",685soc_stats->err_ring_pkts);686len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n",687soc_stats->invalid_rbm);688len += scnprintf(buf + len, size - len, "RXDMA errors:\n");689for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)690len += scnprintf(buf + len, size - len, "%s: %u\n",691rxdma_err[i], soc_stats->rxdma_error[i]);692693len += scnprintf(buf + len, size - len, "\nREO errors:\n");694for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)695len += scnprintf(buf + len, size - len, "%s: %u\n",696reo_err[i], soc_stats->reo_error[i]);697698len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");699len += scnprintf(buf + len, size - len,700"ring0: %u\nring1: %u\nring2: %u\nring3: %u\n",701soc_stats->hal_reo_error[0],702soc_stats->hal_reo_error[1],703soc_stats->hal_reo_error[2],704soc_stats->hal_reo_error[3]);705706len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n");707len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");708709for (i = 0; i < ab->hw_params.max_tx_ring; i++)710len += scnprintf(buf + len, size - len, "ring%d: %u\n",711i, soc_stats->tx_err.desc_na[i]);712713len += scnprintf(buf + len, size - len,714"\nMisc Transmit Failures: %d\n",715atomic_read(&soc_stats->tx_err.misc_fail));716717len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len);718719if (len > size)720len = size;721retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);722kfree(buf);723724return retval;725}726727static const struct file_operations fops_soc_dp_stats = {728.read = ath11k_debugfs_dump_soc_dp_stats,729.open = simple_open,730.owner = THIS_MODULE,731.llseek = default_llseek,732};733734static ssize_t ath11k_write_fw_dbglog(struct file *file,735const char __user *user_buf,736size_t count, loff_t *ppos)737{738struct ath11k *ar = file->private_data;739char buf[128] = {};740struct ath11k_fw_dbglog dbglog;741unsigned int param, mod_id_index, is_end;742u64 value;743int ret, num;744745ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,746user_buf, count);747if (ret <= 0)748return ret;749750num = sscanf(buf, "%u %llx %u %u", ¶m, &value, &mod_id_index, &is_end);751752if (num < 2)753return -EINVAL;754755mutex_lock(&ar->conf_mutex);756if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP ||757param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) {758if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) {759ret = -EINVAL;760goto out;761}762ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value);763if (!is_end) {764ret = count;765goto out;766}767} else {768if (num != 2) {769ret = -EINVAL;770goto out;771}772}773774dbglog.param = param;775dbglog.value = lower_32_bits(value);776ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog);777if (ret) {778ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n",779ret);780goto out;781}782783ret = count;784785out:786mutex_unlock(&ar->conf_mutex);787return ret;788}789790static const struct file_operations fops_fw_dbglog = {791.write = ath11k_write_fw_dbglog,792.open = simple_open,793.owner = THIS_MODULE,794.llseek = default_llseek,795};796797static int ath11k_open_sram_dump(struct inode *inode, struct file *file)798{799struct ath11k_base *ab = inode->i_private;800u8 *buf;801u32 start, end;802int ret;803804start = ab->hw_params.sram_dump.start;805end = ab->hw_params.sram_dump.end;806807buf = vmalloc(end - start + 1);808if (!buf)809return -ENOMEM;810811ret = ath11k_hif_read(ab, buf, start, end);812if (ret) {813ath11k_warn(ab, "failed to dump sram: %d\n", ret);814vfree(buf);815return ret;816}817818file->private_data = buf;819return 0;820}821822static ssize_t ath11k_read_sram_dump(struct file *file,823char __user *user_buf,824size_t count, loff_t *ppos)825{826struct ath11k_base *ab = file->f_inode->i_private;827const char *buf = file->private_data;828int len;829u32 start, end;830831start = ab->hw_params.sram_dump.start;832end = ab->hw_params.sram_dump.end;833len = end - start + 1;834835return simple_read_from_buffer(user_buf, count, ppos, buf, len);836}837838static int ath11k_release_sram_dump(struct inode *inode, struct file *file)839{840vfree(file->private_data);841file->private_data = NULL;842843return 0;844}845846static const struct file_operations fops_sram_dump = {847.open = ath11k_open_sram_dump,848.read = ath11k_read_sram_dump,849.release = ath11k_release_sram_dump,850.owner = THIS_MODULE,851.llseek = default_llseek,852};853854int ath11k_debugfs_pdev_create(struct ath11k_base *ab)855{856if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))857return 0;858859debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,860&fops_simulate_fw_crash);861862debugfs_create_file("soc_dp_stats", 0400, ab->debugfs_soc, ab,863&fops_soc_dp_stats);864865if (ab->hw_params.sram_dump.start != 0)866debugfs_create_file("sram", 0400, ab->debugfs_soc, ab,867&fops_sram_dump);868869return 0;870}871872void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab)873{874debugfs_remove_recursive(ab->debugfs_soc);875ab->debugfs_soc = NULL;876}877878int ath11k_debugfs_soc_create(struct ath11k_base *ab)879{880struct dentry *root;881bool dput_needed;882char name[64];883int ret;884885root = debugfs_lookup("ath11k", NULL);886if (!root) {887root = debugfs_create_dir("ath11k", NULL);888if (IS_ERR_OR_NULL(root))889return PTR_ERR(root);890891dput_needed = false;892} else {893/* a dentry from lookup() needs dput() after we don't use it */894dput_needed = true;895}896897scnprintf(name, sizeof(name), "%s-%s", ath11k_bus_str(ab->hif.bus),898dev_name(ab->dev));899900ab->debugfs_soc = debugfs_create_dir(name, root);901if (IS_ERR_OR_NULL(ab->debugfs_soc)) {902ret = PTR_ERR(ab->debugfs_soc);903goto out;904}905906ret = 0;907908out:909if (dput_needed)910dput(root);911912return ret;913}914915void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)916{917debugfs_remove_recursive(ab->debugfs_soc);918ab->debugfs_soc = NULL;919920/* We are not removing ath11k directory on purpose, even if it921* would be empty. This simplifies the directory handling and it's922* a minor cosmetic issue to leave an empty ath11k directory to923* debugfs.924*/925}926EXPORT_SYMBOL(ath11k_debugfs_soc_destroy);927928void ath11k_debugfs_fw_stats_init(struct ath11k *ar)929{930struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",931ar->debug.debugfs_pdev);932933ar->fw_stats.debugfs_fwstats = fwstats_dir;934935/* all stats debugfs files created are under "fw_stats" directory936* created per PDEV937*/938debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,939&fops_pdev_stats);940debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,941&fops_vdev_stats);942debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,943&fops_bcn_stats);944}945946static ssize_t ath11k_write_pktlog_filter(struct file *file,947const char __user *ubuf,948size_t count, loff_t *ppos)949{950struct ath11k *ar = file->private_data;951struct ath11k_base *ab = ar->ab;952struct htt_rx_ring_tlv_filter tlv_filter = {};953u32 rx_filter = 0, ring_id, filter, mode;954u8 buf[128] = {};955int i, ret, rx_buf_sz = 0;956ssize_t rc;957958mutex_lock(&ar->conf_mutex);959if (ar->state != ATH11K_STATE_ON) {960ret = -ENETDOWN;961goto out;962}963964rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);965if (rc < 0) {966ret = rc;967goto out;968}969buf[rc] = '\0';970971ret = sscanf(buf, "0x%x %u", &filter, &mode);972if (ret != 2) {973ret = -EINVAL;974goto out;975}976977if (filter) {978ret = ath11k_wmi_pdev_pktlog_enable(ar, filter);979if (ret) {980ath11k_warn(ar->ab,981"failed to enable pktlog filter %x: %d\n",982ar->debug.pktlog_filter, ret);983goto out;984}985} else {986ret = ath11k_wmi_pdev_pktlog_disable(ar);987if (ret) {988ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret);989goto out;990}991}992993/* Clear rx filter set for monitor mode and rx status */994for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {995ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;996ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,997HAL_RXDMA_MONITOR_STATUS,998rx_buf_sz, &tlv_filter);999if (ret) {1000ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");1001goto out;1002}1003}1004#define HTT_RX_FILTER_TLV_LITE_MODE \1005(HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \1006HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \1007HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \1008HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \1009HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \1010HTT_RX_FILTER_TLV_FLAGS_MPDU_START)10111012if (mode == ATH11K_PKTLOG_MODE_FULL) {1013rx_filter = HTT_RX_FILTER_TLV_LITE_MODE |1014HTT_RX_FILTER_TLV_FLAGS_MSDU_START |1015HTT_RX_FILTER_TLV_FLAGS_MSDU_END |1016HTT_RX_FILTER_TLV_FLAGS_MPDU_END |1017HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER |1018HTT_RX_FILTER_TLV_FLAGS_ATTENTION;1019rx_buf_sz = DP_RX_BUFFER_SIZE;1020} else if (mode == ATH11K_PKTLOG_MODE_LITE) {1021ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,1022HTT_PPDU_STATS_TAG_PKTLOG);1023if (ret) {1024ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret);1025goto out;1026}10271028rx_filter = HTT_RX_FILTER_TLV_LITE_MODE;1029rx_buf_sz = DP_RX_BUFFER_SIZE_LITE;1030} else {1031rx_buf_sz = DP_RX_BUFFER_SIZE;1032tlv_filter = ath11k_mac_mon_status_filter_default;1033rx_filter = tlv_filter.rx_filter;10341035ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,1036HTT_PPDU_STATS_TAG_DEFAULT);1037if (ret) {1038ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n",1039ret);1040goto out;1041}1042}10431044tlv_filter.rx_filter = rx_filter;1045if (rx_filter) {1046tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;1047tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;1048tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;1049tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |1050HTT_RX_FP_DATA_FILTER_FLASG3;1051}10521053for (i = 0; i < ab->hw_params.num_rxdma_per_pdev; i++) {1054ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;1055ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id,1056ar->dp.mac_id + i,1057HAL_RXDMA_MONITOR_STATUS,1058rx_buf_sz, &tlv_filter);10591060if (ret) {1061ath11k_warn(ab, "failed to set rx filter for monitor status ring\n");1062goto out;1063}1064}10651066ath11k_info(ab, "pktlog mode %s\n",1067((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite"));10681069ar->debug.pktlog_filter = filter;1070ar->debug.pktlog_mode = mode;1071ret = count;10721073out:1074mutex_unlock(&ar->conf_mutex);1075return ret;1076}10771078static ssize_t ath11k_read_pktlog_filter(struct file *file,1079char __user *ubuf,1080size_t count, loff_t *ppos)10811082{1083char buf[32] = {};1084struct ath11k *ar = file->private_data;1085int len = 0;10861087mutex_lock(&ar->conf_mutex);1088len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n",1089ar->debug.pktlog_filter,1090ar->debug.pktlog_mode);1091mutex_unlock(&ar->conf_mutex);10921093return simple_read_from_buffer(ubuf, count, ppos, buf, len);1094}10951096static const struct file_operations fops_pktlog_filter = {1097.read = ath11k_read_pktlog_filter,1098.write = ath11k_write_pktlog_filter,1099.open = simple_open1100};11011102static ssize_t ath11k_write_simulate_radar(struct file *file,1103const char __user *user_buf,1104size_t count, loff_t *ppos)1105{1106struct ath11k *ar = file->private_data;1107int ret;11081109ret = ath11k_wmi_simulate_radar(ar);1110if (ret)1111return ret;11121113return count;1114}11151116static const struct file_operations fops_simulate_radar = {1117.write = ath11k_write_simulate_radar,1118.open = simple_open1119};11201121static ssize_t ath11k_debug_dump_dbr_entries(struct file *file,1122char __user *user_buf,1123size_t count, loff_t *ppos)1124{1125struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data;1126static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"};1127int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100;1128char *buf;1129int i, ret;1130int len = 0;11311132buf = kzalloc(size, GFP_KERNEL);1133if (!buf)1134return -ENOMEM;11351136len += scnprintf(buf + len, size - len,1137"-----------------------------------------\n");1138len += scnprintf(buf + len, size - len,1139"| idx | hp | tp | timestamp | event |\n");1140len += scnprintf(buf + len, size - len,1141"-----------------------------------------\n");11421143spin_lock_bh(&dbr_dbg_data->lock);11441145for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) {1146len += scnprintf(buf + len, size - len,1147"|%4u|%8u|%8u|%11llu|%8s|\n", i,1148dbr_dbg_data->entries[i].hp,1149dbr_dbg_data->entries[i].tp,1150dbr_dbg_data->entries[i].timestamp,1151event_id_to_string[dbr_dbg_data->entries[i].event]);1152}11531154spin_unlock_bh(&dbr_dbg_data->lock);11551156ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);1157kfree(buf);11581159return ret;1160}11611162static const struct file_operations fops_debug_dump_dbr_entries = {1163.read = ath11k_debug_dump_dbr_entries,1164.open = simple_open,1165.owner = THIS_MODULE,1166.llseek = default_llseek,1167};11681169static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id)1170{1171struct ath11k_debug_dbr *dbr_debug;1172struct ath11k_dbg_dbr_data *dbr_dbg_data;11731174if (!ar->debug.dbr_debug[dbr_id])1175return;11761177dbr_debug = ar->debug.dbr_debug[dbr_id];1178dbr_dbg_data = &dbr_debug->dbr_dbg_data;11791180debugfs_remove_recursive(dbr_debug->dbr_debugfs);1181kfree(dbr_dbg_data->entries);1182kfree(dbr_debug);1183ar->debug.dbr_debug[dbr_id] = NULL;1184}11851186static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id)1187{1188struct ath11k_debug_dbr *dbr_debug;1189struct ath11k_dbg_dbr_data *dbr_dbg_data;1190static const char * const dbr_id_to_str[] = {"spectral", "CFR"};11911192if (ar->debug.dbr_debug[dbr_id])1193return 0;11941195ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug),1196GFP_KERNEL);11971198if (!ar->debug.dbr_debug[dbr_id])1199return -ENOMEM;12001201dbr_debug = ar->debug.dbr_debug[dbr_id];1202dbr_dbg_data = &dbr_debug->dbr_dbg_data;12031204if (dbr_debug->dbr_debugfs)1205return 0;12061207dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id],1208ar->debug.debugfs_pdev);1209if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) {1210if (IS_ERR(dbr_debug->dbr_debugfs))1211return PTR_ERR(dbr_debug->dbr_debugfs);1212return -ENOMEM;1213}12141215dbr_debug->dbr_debug_enabled = true;1216dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX;1217dbr_dbg_data->dbr_debug_idx = 0;1218dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX,1219sizeof(struct ath11k_dbg_dbr_entry),1220GFP_KERNEL);1221if (!dbr_dbg_data->entries)1222return -ENOMEM;12231224spin_lock_init(&dbr_dbg_data->lock);12251226debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs,1227dbr_dbg_data, &fops_debug_dump_dbr_entries);12281229return 0;1230}12311232static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file,1233const char __user *ubuf,1234size_t count, loff_t *ppos)1235{1236struct ath11k *ar = file->private_data;1237char buf[32] = {};1238u32 dbr_id, enable;1239int ret;12401241mutex_lock(&ar->conf_mutex);12421243if (ar->state != ATH11K_STATE_ON) {1244ret = -ENETDOWN;1245goto out;1246}12471248ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1249if (ret < 0)1250goto out;12511252buf[ret] = '\0';1253ret = sscanf(buf, "%u %u", &dbr_id, &enable);1254if (ret != 2 || dbr_id > 1 || enable > 1) {1255ret = -EINVAL;1256ath11k_warn(ar->ab, "usage: echo <dbr_id> <val> dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n");1257goto out;1258}12591260if (enable) {1261ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id);1262if (ret) {1263ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n",1264ret);1265goto out;1266}1267} else {1268ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id);1269}12701271ret = count;1272out:1273mutex_unlock(&ar->conf_mutex);1274return ret;1275}12761277static const struct file_operations fops_dbr_debug = {1278.write = ath11k_debugfs_write_enable_dbr_dbg,1279.open = simple_open,1280.owner = THIS_MODULE,1281.llseek = default_llseek,1282};12831284static ssize_t ath11k_write_ps_timekeeper_enable(struct file *file,1285const char __user *user_buf,1286size_t count, loff_t *ppos)1287{1288struct ath11k *ar = file->private_data;1289ssize_t ret;1290u8 ps_timekeeper_enable;12911292if (kstrtou8_from_user(user_buf, count, 0, &ps_timekeeper_enable))1293return -EINVAL;12941295mutex_lock(&ar->conf_mutex);12961297if (ar->state != ATH11K_STATE_ON) {1298ret = -ENETDOWN;1299goto exit;1300}13011302if (!ar->ps_state_enable) {1303ret = -EINVAL;1304goto exit;1305}13061307ar->ps_timekeeper_enable = !!ps_timekeeper_enable;1308ret = count;1309exit:1310mutex_unlock(&ar->conf_mutex);13111312return ret;1313}13141315static ssize_t ath11k_read_ps_timekeeper_enable(struct file *file,1316char __user *user_buf,1317size_t count, loff_t *ppos)1318{1319struct ath11k *ar = file->private_data;1320char buf[32];1321int len;13221323mutex_lock(&ar->conf_mutex);1324len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_timekeeper_enable);1325mutex_unlock(&ar->conf_mutex);13261327return simple_read_from_buffer(user_buf, count, ppos, buf, len);1328}13291330static const struct file_operations fops_ps_timekeeper_enable = {1331.read = ath11k_read_ps_timekeeper_enable,1332.write = ath11k_write_ps_timekeeper_enable,1333.open = simple_open,1334.owner = THIS_MODULE,1335.llseek = default_llseek,1336};13371338static void ath11k_reset_peer_ps_duration(void *data,1339struct ieee80211_sta *sta)1340{1341struct ath11k *ar = data;1342struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);13431344spin_lock_bh(&ar->data_lock);1345arsta->ps_total_duration = 0;1346spin_unlock_bh(&ar->data_lock);1347}13481349static ssize_t ath11k_write_reset_ps_duration(struct file *file,1350const char __user *user_buf,1351size_t count, loff_t *ppos)1352{1353struct ath11k *ar = file->private_data;1354int ret;1355u8 reset_ps_duration;13561357if (kstrtou8_from_user(user_buf, count, 0, &reset_ps_duration))1358return -EINVAL;13591360mutex_lock(&ar->conf_mutex);13611362if (ar->state != ATH11K_STATE_ON) {1363ret = -ENETDOWN;1364goto exit;1365}13661367if (!ar->ps_state_enable) {1368ret = -EINVAL;1369goto exit;1370}13711372ieee80211_iterate_stations_atomic(ar->hw,1373ath11k_reset_peer_ps_duration,1374ar);13751376ret = count;1377exit:1378mutex_unlock(&ar->conf_mutex);1379return ret;1380}13811382static const struct file_operations fops_reset_ps_duration = {1383.write = ath11k_write_reset_ps_duration,1384.open = simple_open,1385.owner = THIS_MODULE,1386.llseek = default_llseek,1387};13881389static void ath11k_peer_ps_state_disable(void *data,1390struct ieee80211_sta *sta)1391{1392struct ath11k *ar = data;1393struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);13941395spin_lock_bh(&ar->data_lock);1396arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;1397arsta->ps_start_time = 0;1398arsta->ps_total_duration = 0;1399spin_unlock_bh(&ar->data_lock);1400}14011402static ssize_t ath11k_write_ps_state_enable(struct file *file,1403const char __user *user_buf,1404size_t count, loff_t *ppos)1405{1406struct ath11k *ar = file->private_data;1407struct ath11k_pdev *pdev = ar->pdev;1408int ret;1409u32 param;1410u8 ps_state_enable;14111412if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))1413return -EINVAL;14141415mutex_lock(&ar->conf_mutex);14161417ps_state_enable = !!ps_state_enable;14181419if (ar->ps_state_enable == ps_state_enable) {1420ret = count;1421goto exit;1422}14231424param = WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE;1425ret = ath11k_wmi_pdev_set_param(ar, param, ps_state_enable, pdev->pdev_id);1426if (ret) {1427ath11k_warn(ar->ab, "failed to enable ps_state_enable: %d\n",1428ret);1429goto exit;1430}1431ar->ps_state_enable = ps_state_enable;14321433if (!ar->ps_state_enable) {1434ar->ps_timekeeper_enable = false;1435ieee80211_iterate_stations_atomic(ar->hw,1436ath11k_peer_ps_state_disable,1437ar);1438}14391440ret = count;14411442exit:1443mutex_unlock(&ar->conf_mutex);14441445return ret;1446}14471448static ssize_t ath11k_read_ps_state_enable(struct file *file,1449char __user *user_buf,1450size_t count, loff_t *ppos)1451{1452struct ath11k *ar = file->private_data;1453char buf[32];1454int len;14551456mutex_lock(&ar->conf_mutex);1457len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_state_enable);1458mutex_unlock(&ar->conf_mutex);14591460return simple_read_from_buffer(user_buf, count, ppos, buf, len);1461}14621463static const struct file_operations fops_ps_state_enable = {1464.read = ath11k_read_ps_state_enable,1465.write = ath11k_write_ps_state_enable,1466.open = simple_open,1467.owner = THIS_MODULE,1468.llseek = default_llseek,1469};14701471int ath11k_debugfs_register(struct ath11k *ar)1472{1473struct ath11k_base *ab = ar->ab;1474char pdev_name[10];1475char buf[100] = {};14761477snprintf(pdev_name, sizeof(pdev_name), "%s%u", "mac", ar->pdev_idx);14781479ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc);1480if (IS_ERR(ar->debug.debugfs_pdev))1481return PTR_ERR(ar->debug.debugfs_pdev);14821483/* Create a symlink under ieee80211/phy* */1484snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev);1485debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf);14861487ath11k_debugfs_htt_stats_init(ar);14881489ath11k_debugfs_fw_stats_init(ar);14901491debugfs_create_file("ext_tx_stats", 0644,1492ar->debug.debugfs_pdev, ar,1493&fops_extd_tx_stats);1494debugfs_create_file("ext_rx_stats", 0644,1495ar->debug.debugfs_pdev, ar,1496&fops_extd_rx_stats);1497debugfs_create_file("pktlog_filter", 0644,1498ar->debug.debugfs_pdev, ar,1499&fops_pktlog_filter);1500debugfs_create_file("fw_dbglog_config", 0600,1501ar->debug.debugfs_pdev, ar,1502&fops_fw_dbglog);15031504if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) {1505debugfs_create_file("dfs_simulate_radar", 0200,1506ar->debug.debugfs_pdev, ar,1507&fops_simulate_radar);1508debugfs_create_bool("dfs_block_radar_events", 0200,1509ar->debug.debugfs_pdev,1510&ar->dfs_block_radar_events);1511}15121513if (ab->hw_params.dbr_debug_support)1514debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev,1515ar, &fops_dbr_debug);15161517debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_pdev, ar,1518&fops_ps_state_enable);15191520if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT,1521ar->ab->wmi_ab.svc_map)) {1522debugfs_create_file("ps_timekeeper_enable", 0600,1523ar->debug.debugfs_pdev, ar,1524&fops_ps_timekeeper_enable);15251526debugfs_create_file("reset_ps_duration", 0200,1527ar->debug.debugfs_pdev, ar,1528&fops_reset_ps_duration);1529}15301531return 0;1532}15331534void ath11k_debugfs_unregister(struct ath11k *ar)1535{1536struct ath11k_debug_dbr *dbr_debug;1537struct ath11k_dbg_dbr_data *dbr_dbg_data;1538int i;15391540for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) {1541dbr_debug = ar->debug.dbr_debug[i];1542if (!dbr_debug)1543continue;15441545dbr_dbg_data = &dbr_debug->dbr_dbg_data;1546kfree(dbr_dbg_data->entries);1547debugfs_remove_recursive(dbr_debug->dbr_debugfs);1548kfree(dbr_debug);1549ar->debug.dbr_debug[i] = NULL;1550}1551}15521553static ssize_t ath11k_write_twt_add_dialog(struct file *file,1554const char __user *ubuf,1555size_t count, loff_t *ppos)1556{1557struct ath11k_vif *arvif = file->private_data;1558struct wmi_twt_add_dialog_params params = {};1559struct wmi_twt_enable_params twt_params = {};1560struct ath11k *ar = arvif->ar;1561u8 buf[128] = {};1562int ret;15631564if (ar->twt_enabled == 0) {1565ath11k_err(ar->ab, "twt support is not enabled\n");1566return -EOPNOTSUPP;1567}15681569ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1570if (ret < 0)1571return ret;15721573buf[ret] = '\0';1574ret = sscanf(buf,1575"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu",1576¶ms.peer_macaddr[0],1577¶ms.peer_macaddr[1],1578¶ms.peer_macaddr[2],1579¶ms.peer_macaddr[3],1580¶ms.peer_macaddr[4],1581¶ms.peer_macaddr[5],1582¶ms.dialog_id,1583¶ms.wake_intvl_us,1584¶ms.wake_intvl_mantis,1585¶ms.wake_dura_us,1586¶ms.sp_offset_us,1587¶ms.twt_cmd,1588¶ms.flag_bcast,1589¶ms.flag_trigger,1590¶ms.flag_flow_type,1591¶ms.flag_protection);1592if (ret != 16)1593return -EINVAL;15941595/* In the case of station vif, TWT is entirely handled by1596* the firmware based on the input parameters in the TWT enable1597* WMI command that is sent to the target during assoc.1598* For manually testing the TWT feature, we need to first disable1599* TWT and send enable command again with TWT input parameter1600* sta_cong_timer_ms set to 0.1601*/1602if (arvif->vif->type == NL80211_IFTYPE_STATION) {1603ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);16041605ath11k_wmi_fill_default_twt_params(&twt_params);1606twt_params.sta_cong_timer_ms = 0;16071608ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);1609}16101611params.vdev_id = arvif->vdev_id;16121613ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, ¶ms);1614if (ret)1615goto err_twt_add_dialog;16161617return count;16181619err_twt_add_dialog:1620if (arvif->vif->type == NL80211_IFTYPE_STATION) {1621ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);1622ath11k_wmi_fill_default_twt_params(&twt_params);1623ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);1624}16251626return ret;1627}16281629static ssize_t ath11k_write_twt_del_dialog(struct file *file,1630const char __user *ubuf,1631size_t count, loff_t *ppos)1632{1633struct ath11k_vif *arvif = file->private_data;1634struct wmi_twt_del_dialog_params params = {};1635struct wmi_twt_enable_params twt_params = {};1636struct ath11k *ar = arvif->ar;1637u8 buf[64] = {};1638int ret;16391640if (ar->twt_enabled == 0) {1641ath11k_err(ar->ab, "twt support is not enabled\n");1642return -EOPNOTSUPP;1643}16441645ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1646if (ret < 0)1647return ret;16481649buf[ret] = '\0';1650ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",1651¶ms.peer_macaddr[0],1652¶ms.peer_macaddr[1],1653¶ms.peer_macaddr[2],1654¶ms.peer_macaddr[3],1655¶ms.peer_macaddr[4],1656¶ms.peer_macaddr[5],1657¶ms.dialog_id);1658if (ret != 7)1659return -EINVAL;16601661params.vdev_id = arvif->vdev_id;16621663ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, ¶ms);1664if (ret)1665return ret;16661667if (arvif->vif->type == NL80211_IFTYPE_STATION) {1668ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);1669ath11k_wmi_fill_default_twt_params(&twt_params);1670ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);1671}16721673return count;1674}16751676static ssize_t ath11k_write_twt_pause_dialog(struct file *file,1677const char __user *ubuf,1678size_t count, loff_t *ppos)1679{1680struct ath11k_vif *arvif = file->private_data;1681struct wmi_twt_pause_dialog_params params = {};1682u8 buf[64] = {};1683int ret;16841685if (arvif->ar->twt_enabled == 0) {1686ath11k_err(arvif->ar->ab, "twt support is not enabled\n");1687return -EOPNOTSUPP;1688}16891690ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1691if (ret < 0)1692return ret;16931694buf[ret] = '\0';1695ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",1696¶ms.peer_macaddr[0],1697¶ms.peer_macaddr[1],1698¶ms.peer_macaddr[2],1699¶ms.peer_macaddr[3],1700¶ms.peer_macaddr[4],1701¶ms.peer_macaddr[5],1702¶ms.dialog_id);1703if (ret != 7)1704return -EINVAL;17051706params.vdev_id = arvif->vdev_id;17071708ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, ¶ms);1709if (ret)1710return ret;17111712return count;1713}17141715static ssize_t ath11k_write_twt_resume_dialog(struct file *file,1716const char __user *ubuf,1717size_t count, loff_t *ppos)1718{1719struct ath11k_vif *arvif = file->private_data;1720struct wmi_twt_resume_dialog_params params = {};1721u8 buf[64] = {};1722int ret;17231724if (arvif->ar->twt_enabled == 0) {1725ath11k_err(arvif->ar->ab, "twt support is not enabled\n");1726return -EOPNOTSUPP;1727}17281729ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1730if (ret < 0)1731return ret;17321733buf[ret] = '\0';1734ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u",1735¶ms.peer_macaddr[0],1736¶ms.peer_macaddr[1],1737¶ms.peer_macaddr[2],1738¶ms.peer_macaddr[3],1739¶ms.peer_macaddr[4],1740¶ms.peer_macaddr[5],1741¶ms.dialog_id,1742¶ms.sp_offset_us,1743¶ms.next_twt_size);1744if (ret != 9)1745return -EINVAL;17461747params.vdev_id = arvif->vdev_id;17481749ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, ¶ms);1750if (ret)1751return ret;17521753return count;1754}17551756static const struct file_operations ath11k_fops_twt_add_dialog = {1757.write = ath11k_write_twt_add_dialog,1758.open = simple_open1759};17601761static const struct file_operations ath11k_fops_twt_del_dialog = {1762.write = ath11k_write_twt_del_dialog,1763.open = simple_open1764};17651766static const struct file_operations ath11k_fops_twt_pause_dialog = {1767.write = ath11k_write_twt_pause_dialog,1768.open = simple_open1769};17701771static const struct file_operations ath11k_fops_twt_resume_dialog = {1772.write = ath11k_write_twt_resume_dialog,1773.open = simple_open1774};17751776void ath11k_debugfs_op_vif_add(struct ieee80211_hw *hw,1777struct ieee80211_vif *vif)1778{1779struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);1780struct ath11k_base *ab = arvif->ar->ab;1781struct dentry *debugfs_twt;17821783if (arvif->vif->type != NL80211_IFTYPE_AP &&1784!(arvif->vif->type == NL80211_IFTYPE_STATION &&1785test_bit(WMI_TLV_SERVICE_STA_TWT, ab->wmi_ab.svc_map)))1786return;17871788debugfs_twt = debugfs_create_dir("twt",1789arvif->vif->debugfs_dir);1790debugfs_create_file("add_dialog", 0200, debugfs_twt,1791arvif, &ath11k_fops_twt_add_dialog);17921793debugfs_create_file("del_dialog", 0200, debugfs_twt,1794arvif, &ath11k_fops_twt_del_dialog);17951796debugfs_create_file("pause_dialog", 0200, debugfs_twt,1797arvif, &ath11k_fops_twt_pause_dialog);17981799debugfs_create_file("resume_dialog", 0200, debugfs_twt,1800arvif, &ath11k_fops_twt_resume_dialog);1801}1802180318041805