Path: blob/main/sys/contrib/dev/athk/ath11k/debugfs.c
48375 views
// SPDX-License-Identifier: BSD-3-Clause-Clear1/*2* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.3*/45#include <linux/vmalloc.h>67#include "debugfs.h"89#include "core.h"10#include "debug.h"11#include "wmi.h"12#include "hal_rx.h"13#include "dp_tx.h"14#include "debugfs_htt_stats.h"15#include "peer.h"16#include "hif.h"1718static const char *htt_bp_umac_ring[HTT_SW_UMAC_RING_IDX_MAX] = {19"REO2SW1_RING",20"REO2SW2_RING",21"REO2SW3_RING",22"REO2SW4_RING",23"WBM2REO_LINK_RING",24"REO2TCL_RING",25"REO2FW_RING",26"RELEASE_RING",27"PPE_RELEASE_RING",28"TCL2TQM_RING",29"TQM_RELEASE_RING",30"REO_RELEASE_RING",31"WBM2SW0_RELEASE_RING",32"WBM2SW1_RELEASE_RING",33"WBM2SW2_RELEASE_RING",34"WBM2SW3_RELEASE_RING",35"REO_CMD_RING",36"REO_STATUS_RING",37};3839static const char *htt_bp_lmac_ring[HTT_SW_LMAC_RING_IDX_MAX] = {40"FW2RXDMA_BUF_RING",41"FW2RXDMA_STATUS_RING",42"FW2RXDMA_LINK_RING",43"SW2RXDMA_BUF_RING",44"WBM2RXDMA_LINK_RING",45"RXDMA2FW_RING",46"RXDMA2SW_RING",47"RXDMA2RELEASE_RING",48"RXDMA2REO_RING",49"MONITOR_STATUS_RING",50"MONITOR_BUF_RING",51"MONITOR_DESC_RING",52"MONITOR_DEST_RING",53};5455void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,56enum wmi_direct_buffer_module id,57enum ath11k_dbg_dbr_event event,58struct hal_srng *srng)59{60struct ath11k_debug_dbr *dbr_debug;61struct ath11k_dbg_dbr_data *dbr_data;62struct ath11k_dbg_dbr_entry *entry;6364if (id >= WMI_DIRECT_BUF_MAX || event >= ATH11K_DBG_DBR_EVENT_MAX)65return;6667dbr_debug = ar->debug.dbr_debug[id];68if (!dbr_debug)69return;7071if (!dbr_debug->dbr_debug_enabled)72return;7374dbr_data = &dbr_debug->dbr_dbg_data;7576spin_lock_bh(&dbr_data->lock);7778if (dbr_data->entries) {79entry = &dbr_data->entries[dbr_data->dbr_debug_idx];80entry->hp = srng->u.src_ring.hp;81entry->tp = *srng->u.src_ring.tp_addr;82entry->timestamp = jiffies;83entry->event = event;8485dbr_data->dbr_debug_idx++;86if (dbr_data->dbr_debug_idx ==87dbr_data->num_ring_debug_entries)88dbr_data->dbr_debug_idx = 0;89}9091spin_unlock_bh(&dbr_data->lock);92}9394static void ath11k_debugfs_fw_stats_reset(struct ath11k *ar)95{96spin_lock_bh(&ar->data_lock);97ar->fw_stats_done = false;98ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);99ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);100spin_unlock_bh(&ar->data_lock);101}102103void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats)104{105struct ath11k_base *ab = ar->ab;106struct ath11k_pdev *pdev;107bool is_end;108static unsigned int num_vdev, num_bcn;109size_t total_vdevs_started = 0;110int i;111112/* WMI_REQUEST_PDEV_STAT request has been already processed */113114if (stats->stats_id == WMI_REQUEST_RSSI_PER_CHAIN_STAT) {115ar->fw_stats_done = true;116return;117}118119if (stats->stats_id == WMI_REQUEST_VDEV_STAT) {120if (list_empty(&stats->vdevs)) {121ath11k_warn(ab, "empty vdev stats");122return;123}124/* FW sends all the active VDEV stats irrespective of PDEV,125* hence limit until the count of all VDEVs started126*/127for (i = 0; i < ab->num_radios; i++) {128pdev = rcu_dereference(ab->pdevs_active[i]);129if (pdev && pdev->ar)130total_vdevs_started += ar->num_started_vdevs;131}132133is_end = ((++num_vdev) == total_vdevs_started);134135list_splice_tail_init(&stats->vdevs,136&ar->fw_stats.vdevs);137138if (is_end) {139ar->fw_stats_done = true;140num_vdev = 0;141}142return;143}144145if (stats->stats_id == WMI_REQUEST_BCN_STAT) {146if (list_empty(&stats->bcn)) {147ath11k_warn(ab, "empty bcn stats");148return;149}150/* Mark end until we reached the count of all started VDEVs151* within the PDEV152*/153is_end = ((++num_bcn) == ar->num_started_vdevs);154155list_splice_tail_init(&stats->bcn,156&ar->fw_stats.bcn);157158if (is_end) {159ar->fw_stats_done = true;160num_bcn = 0;161}162}163}164165static int ath11k_debugfs_fw_stats_request(struct ath11k *ar,166struct stats_request_params *req_param)167{168struct ath11k_base *ab = ar->ab;169unsigned long timeout, time_left;170int ret;171172lockdep_assert_held(&ar->conf_mutex);173174/* FW stats can get split when exceeding the stats data buffer limit.175* In that case, since there is no end marking for the back-to-back176* received 'update stats' event, we keep a 3 seconds timeout in case,177* fw_stats_done is not marked yet178*/179timeout = jiffies + msecs_to_jiffies(3 * 1000);180181ath11k_debugfs_fw_stats_reset(ar);182183reinit_completion(&ar->fw_stats_complete);184185ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);186187if (ret) {188ath11k_warn(ab, "could not request fw stats (%d)\n",189ret);190return ret;191}192193time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);194195if (!time_left)196return -ETIMEDOUT;197198for (;;) {199if (time_after(jiffies, timeout))200break;201202spin_lock_bh(&ar->data_lock);203if (ar->fw_stats_done) {204spin_unlock_bh(&ar->data_lock);205break;206}207spin_unlock_bh(&ar->data_lock);208}209return 0;210}211212int ath11k_debugfs_get_fw_stats(struct ath11k *ar, u32 pdev_id,213u32 vdev_id, u32 stats_id)214{215struct ath11k_base *ab = ar->ab;216struct stats_request_params req_param;217int ret;218219mutex_lock(&ar->conf_mutex);220221if (ar->state != ATH11K_STATE_ON) {222ret = -ENETDOWN;223goto err_unlock;224}225226req_param.pdev_id = pdev_id;227req_param.vdev_id = vdev_id;228req_param.stats_id = stats_id;229230ret = ath11k_debugfs_fw_stats_request(ar, &req_param);231if (ret)232ath11k_warn(ab, "failed to request fw stats: %d\n", ret);233234ath11k_dbg(ab, ATH11K_DBG_WMI,235"debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",236pdev_id, vdev_id, stats_id);237238err_unlock:239mutex_unlock(&ar->conf_mutex);240241return ret;242}243244static int ath11k_open_pdev_stats(struct inode *inode, struct file *file)245{246struct ath11k *ar = inode->i_private;247struct ath11k_base *ab = ar->ab;248struct stats_request_params req_param;249void *buf = NULL;250int ret;251252mutex_lock(&ar->conf_mutex);253254if (ar->state != ATH11K_STATE_ON) {255ret = -ENETDOWN;256goto err_unlock;257}258259buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);260if (!buf) {261ret = -ENOMEM;262goto err_unlock;263}264265req_param.pdev_id = ar->pdev->pdev_id;266req_param.vdev_id = 0;267req_param.stats_id = WMI_REQUEST_PDEV_STAT;268269ret = ath11k_debugfs_fw_stats_request(ar, &req_param);270if (ret) {271ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);272goto err_free;273}274275ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);276277file->private_data = buf;278279mutex_unlock(&ar->conf_mutex);280return 0;281282err_free:283vfree(buf);284285err_unlock:286mutex_unlock(&ar->conf_mutex);287return ret;288}289290static int ath11k_release_pdev_stats(struct inode *inode, struct file *file)291{292vfree(file->private_data);293294return 0;295}296297static ssize_t ath11k_read_pdev_stats(struct file *file,298char __user *user_buf,299size_t count, loff_t *ppos)300{301const char *buf = file->private_data;302size_t len = strlen(buf);303304return simple_read_from_buffer(user_buf, count, ppos, buf, len);305}306307static const struct file_operations fops_pdev_stats = {308.open = ath11k_open_pdev_stats,309.release = ath11k_release_pdev_stats,310.read = ath11k_read_pdev_stats,311.owner = THIS_MODULE,312.llseek = default_llseek,313};314315static int ath11k_open_vdev_stats(struct inode *inode, struct file *file)316{317struct ath11k *ar = inode->i_private;318struct stats_request_params req_param;319void *buf = NULL;320int ret;321322mutex_lock(&ar->conf_mutex);323324if (ar->state != ATH11K_STATE_ON) {325ret = -ENETDOWN;326goto err_unlock;327}328329buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);330if (!buf) {331ret = -ENOMEM;332goto err_unlock;333}334335req_param.pdev_id = ar->pdev->pdev_id;336/* VDEV stats is always sent for all active VDEVs from FW */337req_param.vdev_id = 0;338req_param.stats_id = WMI_REQUEST_VDEV_STAT;339340ret = ath11k_debugfs_fw_stats_request(ar, &req_param);341if (ret) {342ath11k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret);343goto err_free;344}345346ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);347348file->private_data = buf;349350mutex_unlock(&ar->conf_mutex);351return 0;352353err_free:354vfree(buf);355356err_unlock:357mutex_unlock(&ar->conf_mutex);358return ret;359}360361static int ath11k_release_vdev_stats(struct inode *inode, struct file *file)362{363vfree(file->private_data);364365return 0;366}367368static ssize_t ath11k_read_vdev_stats(struct file *file,369char __user *user_buf,370size_t count, loff_t *ppos)371{372const char *buf = file->private_data;373size_t len = strlen(buf);374375return simple_read_from_buffer(user_buf, count, ppos, buf, len);376}377378static const struct file_operations fops_vdev_stats = {379.open = ath11k_open_vdev_stats,380.release = ath11k_release_vdev_stats,381.read = ath11k_read_vdev_stats,382.owner = THIS_MODULE,383.llseek = default_llseek,384};385386static int ath11k_open_bcn_stats(struct inode *inode, struct file *file)387{388struct ath11k *ar = inode->i_private;389struct ath11k_vif *arvif;390struct stats_request_params req_param;391void *buf = NULL;392int ret;393394mutex_lock(&ar->conf_mutex);395396if (ar->state != ATH11K_STATE_ON) {397ret = -ENETDOWN;398goto err_unlock;399}400401buf = vmalloc(ATH11K_FW_STATS_BUF_SIZE);402if (!buf) {403ret = -ENOMEM;404goto err_unlock;405}406407req_param.stats_id = WMI_REQUEST_BCN_STAT;408req_param.pdev_id = ar->pdev->pdev_id;409410/* loop all active VDEVs for bcn stats */411list_for_each_entry(arvif, &ar->arvifs, list) {412if (!arvif->is_up)413continue;414415req_param.vdev_id = arvif->vdev_id;416ret = ath11k_debugfs_fw_stats_request(ar, &req_param);417if (ret) {418ath11k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret);419goto err_free;420}421}422423ath11k_wmi_fw_stats_fill(ar, &ar->fw_stats, req_param.stats_id, buf);424425/* since beacon stats request is looped for all active VDEVs, saved fw426* stats is not freed for each request until done for all active VDEVs427*/428spin_lock_bh(&ar->data_lock);429ath11k_fw_stats_bcn_free(&ar->fw_stats.bcn);430spin_unlock_bh(&ar->data_lock);431432file->private_data = buf;433434mutex_unlock(&ar->conf_mutex);435return 0;436437err_free:438vfree(buf);439440err_unlock:441mutex_unlock(&ar->conf_mutex);442return ret;443}444445static int ath11k_release_bcn_stats(struct inode *inode, struct file *file)446{447vfree(file->private_data);448449return 0;450}451452static ssize_t ath11k_read_bcn_stats(struct file *file,453char __user *user_buf,454size_t count, loff_t *ppos)455{456const char *buf = file->private_data;457size_t len = strlen(buf);458459return simple_read_from_buffer(user_buf, count, ppos, buf, len);460}461462static const struct file_operations fops_bcn_stats = {463.open = ath11k_open_bcn_stats,464.release = ath11k_release_bcn_stats,465.read = ath11k_read_bcn_stats,466.owner = THIS_MODULE,467.llseek = default_llseek,468};469470static ssize_t ath11k_read_simulate_fw_crash(struct file *file,471char __user *user_buf,472size_t count, loff_t *ppos)473{474const char buf[] =475"To simulate firmware crash write one of the keywords to this file:\n"476"`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"477"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";478479return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));480}481482/* Simulate firmware crash:483* 'soft': Call wmi command causing firmware hang. This firmware hang is484* recoverable by warm firmware reset.485* 'hard': Force firmware crash by setting any vdev parameter for not allowed486* vdev id. This is hard firmware crash because it is recoverable only by cold487* firmware reset.488*/489static ssize_t ath11k_write_simulate_fw_crash(struct file *file,490const char __user *user_buf,491size_t count, loff_t *ppos)492{493struct ath11k_base *ab = file->private_data;494struct ath11k_pdev *pdev;495struct ath11k *ar = ab->pdevs[0].ar;496char buf[32] = {0};497ssize_t rc;498int i, ret, radioup = 0;499500for (i = 0; i < ab->num_radios; i++) {501pdev = &ab->pdevs[i];502ar = pdev->ar;503if (ar && ar->state == ATH11K_STATE_ON) {504radioup = 1;505break;506}507}508/* filter partial writes and invalid commands */509if (*ppos != 0 || count >= sizeof(buf) || count == 0)510return -EINVAL;511512rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);513if (rc < 0)514return rc;515516/* drop the possible '\n' from the end */517if (buf[*ppos - 1] == '\n')518buf[*ppos - 1] = '\0';519520if (radioup == 0) {521ret = -ENETDOWN;522goto exit;523}524525if (!strcmp(buf, "assert")) {526ath11k_info(ab, "simulating firmware assert crash\n");527ret = ath11k_wmi_force_fw_hang_cmd(ar,528ATH11K_WMI_FW_HANG_ASSERT_TYPE,529ATH11K_WMI_FW_HANG_DELAY);530} else if (!strcmp(buf, "hw-restart")) {531ath11k_info(ab, "user requested hw restart\n");532queue_work(ab->workqueue_aux, &ab->reset_work);533ret = 0;534} else {535ret = -EINVAL;536goto exit;537}538539if (ret) {540ath11k_warn(ab, "failed to simulate firmware crash: %d\n", ret);541goto exit;542}543544ret = count;545546exit:547return ret;548}549550static const struct file_operations fops_simulate_fw_crash = {551.read = ath11k_read_simulate_fw_crash,552.write = ath11k_write_simulate_fw_crash,553.open = simple_open,554.owner = THIS_MODULE,555.llseek = default_llseek,556};557558static ssize_t ath11k_write_enable_extd_tx_stats(struct file *file,559const char __user *ubuf,560size_t count, loff_t *ppos)561{562struct ath11k *ar = file->private_data;563u32 filter;564int ret;565566if (kstrtouint_from_user(ubuf, count, 0, &filter))567return -EINVAL;568569mutex_lock(&ar->conf_mutex);570571if (ar->state != ATH11K_STATE_ON) {572ret = -ENETDOWN;573goto out;574}575576if (filter == ar->debug.extd_tx_stats) {577ret = count;578goto out;579}580581ar->debug.extd_tx_stats = filter;582ret = count;583584out:585mutex_unlock(&ar->conf_mutex);586return ret;587}588589static ssize_t ath11k_read_enable_extd_tx_stats(struct file *file,590char __user *ubuf,591size_t count, loff_t *ppos)592593{594char buf[32] = {0};595struct ath11k *ar = file->private_data;596int len = 0;597598mutex_lock(&ar->conf_mutex);599len = scnprintf(buf, sizeof(buf) - len, "%08x\n",600ar->debug.extd_tx_stats);601mutex_unlock(&ar->conf_mutex);602603return simple_read_from_buffer(ubuf, count, ppos, buf, len);604}605606static const struct file_operations fops_extd_tx_stats = {607.read = ath11k_read_enable_extd_tx_stats,608.write = ath11k_write_enable_extd_tx_stats,609.open = simple_open610};611612static ssize_t ath11k_write_extd_rx_stats(struct file *file,613const char __user *ubuf,614size_t count, loff_t *ppos)615{616struct ath11k *ar = file->private_data;617struct ath11k_base *ab = ar->ab;618struct htt_rx_ring_tlv_filter tlv_filter = {0};619u32 enable, rx_filter = 0, ring_id;620int i;621int ret;622623if (kstrtouint_from_user(ubuf, count, 0, &enable))624return -EINVAL;625626mutex_lock(&ar->conf_mutex);627628if (ar->state != ATH11K_STATE_ON) {629ret = -ENETDOWN;630goto exit;631}632633if (enable > 1) {634ret = -EINVAL;635goto exit;636}637638if (enable == ar->debug.extd_rx_stats) {639ret = count;640goto exit;641}642643if (test_bit(ATH11K_FLAG_MONITOR_STARTED, &ar->monitor_flags)) {644ar->debug.extd_rx_stats = enable;645ret = count;646goto exit;647}648649if (enable) {650rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START;651rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START;652rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END;653rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS;654rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT;655rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE;656657tlv_filter.rx_filter = rx_filter;658tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;659tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;660tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;661tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |662HTT_RX_FP_DATA_FILTER_FLASG3;663} else {664tlv_filter = ath11k_mac_mon_status_filter_default;665}666667ar->debug.rx_filter = tlv_filter.rx_filter;668669for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {670ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;671ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,672HAL_RXDMA_MONITOR_STATUS,673DP_RX_BUFFER_SIZE, &tlv_filter);674675if (ret) {676ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");677goto exit;678}679}680681ar->debug.extd_rx_stats = enable;682ret = count;683exit:684mutex_unlock(&ar->conf_mutex);685return ret;686}687688static ssize_t ath11k_read_extd_rx_stats(struct file *file,689char __user *ubuf,690size_t count, loff_t *ppos)691{692struct ath11k *ar = file->private_data;693char buf[32];694int len = 0;695696mutex_lock(&ar->conf_mutex);697len = scnprintf(buf, sizeof(buf) - len, "%d\n",698ar->debug.extd_rx_stats);699mutex_unlock(&ar->conf_mutex);700701return simple_read_from_buffer(ubuf, count, ppos, buf, len);702}703704static const struct file_operations fops_extd_rx_stats = {705.read = ath11k_read_extd_rx_stats,706.write = ath11k_write_extd_rx_stats,707.open = simple_open,708};709710static int ath11k_fill_bp_stats(struct ath11k_base *ab,711struct ath11k_bp_stats *bp_stats,712char *buf, int len, int size)713{714lockdep_assert_held(&ab->base_lock);715716len += scnprintf(buf + len, size - len, "count: %u\n",717bp_stats->count);718len += scnprintf(buf + len, size - len, "hp: %u\n",719bp_stats->hp);720len += scnprintf(buf + len, size - len, "tp: %u\n",721bp_stats->tp);722len += scnprintf(buf + len, size - len, "seen before: %ums\n\n",723jiffies_to_msecs(jiffies - bp_stats->jiffies));724return len;725}726727static ssize_t ath11k_debugfs_dump_soc_ring_bp_stats(struct ath11k_base *ab,728char *buf, int size)729{730struct ath11k_bp_stats *bp_stats;731bool stats_rxd = false;732u8 i, pdev_idx;733int len = 0;734735len += scnprintf(buf + len, size - len, "\nBackpressure Stats\n");736len += scnprintf(buf + len, size - len, "==================\n");737738spin_lock_bh(&ab->base_lock);739for (i = 0; i < HTT_SW_UMAC_RING_IDX_MAX; i++) {740bp_stats = &ab->soc_stats.bp_stats.umac_ring_bp_stats[i];741742if (!bp_stats->count)743continue;744745len += scnprintf(buf + len, size - len, "Ring: %s\n",746htt_bp_umac_ring[i]);747len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);748stats_rxd = true;749}750751for (i = 0; i < HTT_SW_LMAC_RING_IDX_MAX; i++) {752for (pdev_idx = 0; pdev_idx < MAX_RADIOS; pdev_idx++) {753bp_stats =754&ab->soc_stats.bp_stats.lmac_ring_bp_stats[i][pdev_idx];755756if (!bp_stats->count)757continue;758759len += scnprintf(buf + len, size - len, "Ring: %s\n",760htt_bp_lmac_ring[i]);761len += scnprintf(buf + len, size - len, "pdev: %d\n",762pdev_idx);763len = ath11k_fill_bp_stats(ab, bp_stats, buf, len, size);764stats_rxd = true;765}766}767spin_unlock_bh(&ab->base_lock);768769if (!stats_rxd)770len += scnprintf(buf + len, size - len,771"No Ring Backpressure stats received\n\n");772773return len;774}775776static ssize_t ath11k_debugfs_dump_soc_dp_stats(struct file *file,777char __user *user_buf,778size_t count, loff_t *ppos)779{780struct ath11k_base *ab = file->private_data;781struct ath11k_soc_dp_stats *soc_stats = &ab->soc_stats;782int len = 0, i, retval;783const int size = 4096;784static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = {785"Overflow", "MPDU len", "FCS", "Decrypt", "TKIP MIC",786"Unencrypt", "MSDU len", "MSDU limit", "WiFi parse",787"AMSDU parse", "SA timeout", "DA timeout",788"Flow timeout", "Flush req"};789static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = {790"Desc addr zero", "Desc inval", "AMPDU in non BA",791"Non BA dup", "BA dup", "Frame 2k jump", "BAR 2k jump",792"Frame OOR", "BAR OOR", "No BA session",793"Frame SN equal SSN", "PN check fail", "2k err",794"PN err", "Desc blocked"};795796char *buf;797798buf = kzalloc(size, GFP_KERNEL);799if (!buf)800return -ENOMEM;801802len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n");803len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",804soc_stats->err_ring_pkts);805len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n",806soc_stats->invalid_rbm);807len += scnprintf(buf + len, size - len, "RXDMA errors:\n");808for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++)809len += scnprintf(buf + len, size - len, "%s: %u\n",810rxdma_err[i], soc_stats->rxdma_error[i]);811812len += scnprintf(buf + len, size - len, "\nREO errors:\n");813for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++)814len += scnprintf(buf + len, size - len, "%s: %u\n",815reo_err[i], soc_stats->reo_error[i]);816817len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n");818len += scnprintf(buf + len, size - len,819"ring0: %u\nring1: %u\nring2: %u\nring3: %u\n",820soc_stats->hal_reo_error[0],821soc_stats->hal_reo_error[1],822soc_stats->hal_reo_error[2],823soc_stats->hal_reo_error[3]);824825len += scnprintf(buf + len, size - len, "\nSOC TX STATS:\n");826len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n");827828for (i = 0; i < ab->hw_params.max_tx_ring; i++)829len += scnprintf(buf + len, size - len, "ring%d: %u\n",830i, soc_stats->tx_err.desc_na[i]);831832len += scnprintf(buf + len, size - len,833"\nMisc Transmit Failures: %d\n",834atomic_read(&soc_stats->tx_err.misc_fail));835836len += ath11k_debugfs_dump_soc_ring_bp_stats(ab, buf + len, size - len);837838if (len > size)839len = size;840retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);841kfree(buf);842843return retval;844}845846static const struct file_operations fops_soc_dp_stats = {847.read = ath11k_debugfs_dump_soc_dp_stats,848.open = simple_open,849.owner = THIS_MODULE,850.llseek = default_llseek,851};852853static ssize_t ath11k_write_fw_dbglog(struct file *file,854const char __user *user_buf,855size_t count, loff_t *ppos)856{857struct ath11k *ar = file->private_data;858char buf[128] = {0};859struct ath11k_fw_dbglog dbglog;860unsigned int param, mod_id_index, is_end;861u64 value;862int ret, num;863864ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,865user_buf, count);866if (ret <= 0)867return ret;868869num = sscanf(buf, "%u %llx %u %u", ¶m, &value, &mod_id_index, &is_end);870871if (num < 2)872return -EINVAL;873874mutex_lock(&ar->conf_mutex);875if (param == WMI_DEBUG_LOG_PARAM_MOD_ENABLE_BITMAP ||876param == WMI_DEBUG_LOG_PARAM_WOW_MOD_ENABLE_BITMAP) {877if (num != 4 || mod_id_index > (MAX_MODULE_ID_BITMAP_WORDS - 1)) {878ret = -EINVAL;879goto out;880}881ar->debug.module_id_bitmap[mod_id_index] = upper_32_bits(value);882if (!is_end) {883ret = count;884goto out;885}886} else {887if (num != 2) {888ret = -EINVAL;889goto out;890}891}892893dbglog.param = param;894dbglog.value = lower_32_bits(value);895ret = ath11k_wmi_fw_dbglog_cfg(ar, ar->debug.module_id_bitmap, &dbglog);896if (ret) {897ath11k_warn(ar->ab, "fw dbglog config failed from debugfs: %d\n",898ret);899goto out;900}901902ret = count;903904out:905mutex_unlock(&ar->conf_mutex);906return ret;907}908909static const struct file_operations fops_fw_dbglog = {910.write = ath11k_write_fw_dbglog,911.open = simple_open,912.owner = THIS_MODULE,913.llseek = default_llseek,914};915916static int ath11k_open_sram_dump(struct inode *inode, struct file *file)917{918struct ath11k_base *ab = inode->i_private;919u8 *buf;920u32 start, end;921int ret;922923start = ab->hw_params.sram_dump.start;924end = ab->hw_params.sram_dump.end;925926buf = vmalloc(end - start + 1);927if (!buf)928return -ENOMEM;929930ret = ath11k_hif_read(ab, buf, start, end);931if (ret) {932ath11k_warn(ab, "failed to dump sram: %d\n", ret);933vfree(buf);934return ret;935}936937file->private_data = buf;938return 0;939}940941static ssize_t ath11k_read_sram_dump(struct file *file,942char __user *user_buf,943size_t count, loff_t *ppos)944{945struct ath11k_base *ab = file->f_inode->i_private;946const char *buf = file->private_data;947int len;948u32 start, end;949950start = ab->hw_params.sram_dump.start;951end = ab->hw_params.sram_dump.end;952len = end - start + 1;953954return simple_read_from_buffer(user_buf, count, ppos, buf, len);955}956957static int ath11k_release_sram_dump(struct inode *inode, struct file *file)958{959vfree(file->private_data);960file->private_data = NULL;961962return 0;963}964965static const struct file_operations fops_sram_dump = {966.open = ath11k_open_sram_dump,967.read = ath11k_read_sram_dump,968.release = ath11k_release_sram_dump,969.owner = THIS_MODULE,970.llseek = default_llseek,971};972973int ath11k_debugfs_pdev_create(struct ath11k_base *ab)974{975if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))976return 0;977978debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab,979&fops_simulate_fw_crash);980981debugfs_create_file("soc_dp_stats", 0600, ab->debugfs_soc, ab,982&fops_soc_dp_stats);983984if (ab->hw_params.sram_dump.start != 0)985debugfs_create_file("sram", 0400, ab->debugfs_soc, ab,986&fops_sram_dump);987988return 0;989}990991void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab)992{993debugfs_remove_recursive(ab->debugfs_soc);994ab->debugfs_soc = NULL;995}996997int ath11k_debugfs_soc_create(struct ath11k_base *ab)998{999struct dentry *root;1000bool dput_needed;1001char name[64];1002int ret;10031004root = debugfs_lookup("ath11k", NULL);1005if (!root) {1006root = debugfs_create_dir("ath11k", NULL);1007if (IS_ERR_OR_NULL(root))1008return PTR_ERR(root);10091010dput_needed = false;1011} else {1012/* a dentry from lookup() needs dput() after we don't use it */1013dput_needed = true;1014}10151016scnprintf(name, sizeof(name), "%s-%s", ath11k_bus_str(ab->hif.bus),1017dev_name(ab->dev));10181019ab->debugfs_soc = debugfs_create_dir(name, root);1020if (IS_ERR_OR_NULL(ab->debugfs_soc)) {1021ret = PTR_ERR(ab->debugfs_soc);1022goto out;1023}10241025ret = 0;10261027out:1028if (dput_needed)1029dput(root);10301031return ret;1032}10331034void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)1035{1036debugfs_remove_recursive(ab->debugfs_soc);1037ab->debugfs_soc = NULL;10381039/* We are not removing ath11k directory on purpose, even if it1040* would be empty. This simplifies the directory handling and it's1041* a minor cosmetic issue to leave an empty ath11k directory to1042* debugfs.1043*/1044}1045EXPORT_SYMBOL(ath11k_debugfs_soc_destroy);10461047void ath11k_debugfs_fw_stats_init(struct ath11k *ar)1048{1049struct dentry *fwstats_dir = debugfs_create_dir("fw_stats",1050ar->debug.debugfs_pdev);10511052ar->fw_stats.debugfs_fwstats = fwstats_dir;10531054/* all stats debugfs files created are under "fw_stats" directory1055* created per PDEV1056*/1057debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar,1058&fops_pdev_stats);1059debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar,1060&fops_vdev_stats);1061debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar,1062&fops_bcn_stats);1063}10641065static ssize_t ath11k_write_pktlog_filter(struct file *file,1066const char __user *ubuf,1067size_t count, loff_t *ppos)1068{1069struct ath11k *ar = file->private_data;1070struct ath11k_base *ab = ar->ab;1071struct htt_rx_ring_tlv_filter tlv_filter = {0};1072u32 rx_filter = 0, ring_id, filter, mode;1073u8 buf[128] = {0};1074int i, ret, rx_buf_sz = 0;1075ssize_t rc;10761077mutex_lock(&ar->conf_mutex);1078if (ar->state != ATH11K_STATE_ON) {1079ret = -ENETDOWN;1080goto out;1081}10821083rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1084if (rc < 0) {1085ret = rc;1086goto out;1087}1088buf[rc] = '\0';10891090ret = sscanf(buf, "0x%x %u", &filter, &mode);1091if (ret != 2) {1092ret = -EINVAL;1093goto out;1094}10951096if (filter) {1097ret = ath11k_wmi_pdev_pktlog_enable(ar, filter);1098if (ret) {1099ath11k_warn(ar->ab,1100"failed to enable pktlog filter %x: %d\n",1101ar->debug.pktlog_filter, ret);1102goto out;1103}1104} else {1105ret = ath11k_wmi_pdev_pktlog_disable(ar);1106if (ret) {1107ath11k_warn(ar->ab, "failed to disable pktlog: %d\n", ret);1108goto out;1109}1110}11111112/* Clear rx filter set for monitor mode and rx status */1113for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {1114ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;1115ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id,1116HAL_RXDMA_MONITOR_STATUS,1117rx_buf_sz, &tlv_filter);1118if (ret) {1119ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n");1120goto out;1121}1122}1123#define HTT_RX_FILTER_TLV_LITE_MODE \1124(HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \1125HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \1126HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \1127HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \1128HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \1129HTT_RX_FILTER_TLV_FLAGS_MPDU_START)11301131if (mode == ATH11K_PKTLOG_MODE_FULL) {1132rx_filter = HTT_RX_FILTER_TLV_LITE_MODE |1133HTT_RX_FILTER_TLV_FLAGS_MSDU_START |1134HTT_RX_FILTER_TLV_FLAGS_MSDU_END |1135HTT_RX_FILTER_TLV_FLAGS_MPDU_END |1136HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER |1137HTT_RX_FILTER_TLV_FLAGS_ATTENTION;1138rx_buf_sz = DP_RX_BUFFER_SIZE;1139} else if (mode == ATH11K_PKTLOG_MODE_LITE) {1140ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,1141HTT_PPDU_STATS_TAG_PKTLOG);1142if (ret) {1143ath11k_err(ar->ab, "failed to enable pktlog lite: %d\n", ret);1144goto out;1145}11461147rx_filter = HTT_RX_FILTER_TLV_LITE_MODE;1148rx_buf_sz = DP_RX_BUFFER_SIZE_LITE;1149} else {1150rx_buf_sz = DP_RX_BUFFER_SIZE;1151tlv_filter = ath11k_mac_mon_status_filter_default;1152rx_filter = tlv_filter.rx_filter;11531154ret = ath11k_dp_tx_htt_h2t_ppdu_stats_req(ar,1155HTT_PPDU_STATS_TAG_DEFAULT);1156if (ret) {1157ath11k_err(ar->ab, "failed to send htt ppdu stats req: %d\n",1158ret);1159goto out;1160}1161}11621163tlv_filter.rx_filter = rx_filter;1164if (rx_filter) {1165tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0;1166tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1;1167tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2;1168tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 |1169HTT_RX_FP_DATA_FILTER_FLASG3;1170}11711172for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) {1173ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id;1174ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id,1175ar->dp.mac_id + i,1176HAL_RXDMA_MONITOR_STATUS,1177rx_buf_sz, &tlv_filter);11781179if (ret) {1180ath11k_warn(ab, "failed to set rx filter for monitor status ring\n");1181goto out;1182}1183}11841185ath11k_info(ab, "pktlog mode %s\n",1186((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite"));11871188ar->debug.pktlog_filter = filter;1189ar->debug.pktlog_mode = mode;1190ret = count;11911192out:1193mutex_unlock(&ar->conf_mutex);1194return ret;1195}11961197static ssize_t ath11k_read_pktlog_filter(struct file *file,1198char __user *ubuf,1199size_t count, loff_t *ppos)12001201{1202char buf[32] = {0};1203struct ath11k *ar = file->private_data;1204int len = 0;12051206mutex_lock(&ar->conf_mutex);1207len = scnprintf(buf, sizeof(buf) - len, "%08x %08x\n",1208ar->debug.pktlog_filter,1209ar->debug.pktlog_mode);1210mutex_unlock(&ar->conf_mutex);12111212return simple_read_from_buffer(ubuf, count, ppos, buf, len);1213}12141215static const struct file_operations fops_pktlog_filter = {1216.read = ath11k_read_pktlog_filter,1217.write = ath11k_write_pktlog_filter,1218.open = simple_open1219};12201221static ssize_t ath11k_write_simulate_radar(struct file *file,1222const char __user *user_buf,1223size_t count, loff_t *ppos)1224{1225struct ath11k *ar = file->private_data;1226int ret;12271228ret = ath11k_wmi_simulate_radar(ar);1229if (ret)1230return ret;12311232return count;1233}12341235static const struct file_operations fops_simulate_radar = {1236.write = ath11k_write_simulate_radar,1237.open = simple_open1238};12391240static ssize_t ath11k_debug_dump_dbr_entries(struct file *file,1241char __user *user_buf,1242size_t count, loff_t *ppos)1243{1244struct ath11k_dbg_dbr_data *dbr_dbg_data = file->private_data;1245static const char * const event_id_to_string[] = {"empty", "Rx", "Replenish"};1246int size = ATH11K_DEBUG_DBR_ENTRIES_MAX * 100;1247char *buf;1248int i, ret;1249int len = 0;12501251buf = kzalloc(size, GFP_KERNEL);1252if (!buf)1253return -ENOMEM;12541255len += scnprintf(buf + len, size - len,1256"-----------------------------------------\n");1257len += scnprintf(buf + len, size - len,1258"| idx | hp | tp | timestamp | event |\n");1259len += scnprintf(buf + len, size - len,1260"-----------------------------------------\n");12611262spin_lock_bh(&dbr_dbg_data->lock);12631264for (i = 0; i < dbr_dbg_data->num_ring_debug_entries; i++) {1265len += scnprintf(buf + len, size - len,1266"|%4u|%8u|%8u|%11llu|%8s|\n", i,1267dbr_dbg_data->entries[i].hp,1268dbr_dbg_data->entries[i].tp,1269dbr_dbg_data->entries[i].timestamp,1270event_id_to_string[dbr_dbg_data->entries[i].event]);1271}12721273spin_unlock_bh(&dbr_dbg_data->lock);12741275ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);1276kfree(buf);12771278return ret;1279}12801281static const struct file_operations fops_debug_dump_dbr_entries = {1282.read = ath11k_debug_dump_dbr_entries,1283.open = simple_open,1284.owner = THIS_MODULE,1285.llseek = default_llseek,1286};12871288static void ath11k_debugfs_dbr_dbg_destroy(struct ath11k *ar, int dbr_id)1289{1290struct ath11k_debug_dbr *dbr_debug;1291struct ath11k_dbg_dbr_data *dbr_dbg_data;12921293if (!ar->debug.dbr_debug[dbr_id])1294return;12951296dbr_debug = ar->debug.dbr_debug[dbr_id];1297dbr_dbg_data = &dbr_debug->dbr_dbg_data;12981299debugfs_remove_recursive(dbr_debug->dbr_debugfs);1300kfree(dbr_dbg_data->entries);1301kfree(dbr_debug);1302ar->debug.dbr_debug[dbr_id] = NULL;1303}13041305static int ath11k_debugfs_dbr_dbg_init(struct ath11k *ar, int dbr_id)1306{1307struct ath11k_debug_dbr *dbr_debug;1308struct ath11k_dbg_dbr_data *dbr_dbg_data;1309static const char * const dbr_id_to_str[] = {"spectral", "CFR"};13101311if (ar->debug.dbr_debug[dbr_id])1312return 0;13131314ar->debug.dbr_debug[dbr_id] = kzalloc(sizeof(*dbr_debug),1315GFP_KERNEL);13161317if (!ar->debug.dbr_debug[dbr_id])1318return -ENOMEM;13191320dbr_debug = ar->debug.dbr_debug[dbr_id];1321dbr_dbg_data = &dbr_debug->dbr_dbg_data;13221323if (dbr_debug->dbr_debugfs)1324return 0;13251326dbr_debug->dbr_debugfs = debugfs_create_dir(dbr_id_to_str[dbr_id],1327ar->debug.debugfs_pdev);1328if (IS_ERR_OR_NULL(dbr_debug->dbr_debugfs)) {1329if (IS_ERR(dbr_debug->dbr_debugfs))1330return PTR_ERR(dbr_debug->dbr_debugfs);1331return -ENOMEM;1332}13331334dbr_debug->dbr_debug_enabled = true;1335dbr_dbg_data->num_ring_debug_entries = ATH11K_DEBUG_DBR_ENTRIES_MAX;1336dbr_dbg_data->dbr_debug_idx = 0;1337dbr_dbg_data->entries = kcalloc(ATH11K_DEBUG_DBR_ENTRIES_MAX,1338sizeof(struct ath11k_dbg_dbr_entry),1339GFP_KERNEL);1340if (!dbr_dbg_data->entries)1341return -ENOMEM;13421343spin_lock_init(&dbr_dbg_data->lock);13441345debugfs_create_file("dump_dbr_debug", 0444, dbr_debug->dbr_debugfs,1346dbr_dbg_data, &fops_debug_dump_dbr_entries);13471348return 0;1349}13501351static ssize_t ath11k_debugfs_write_enable_dbr_dbg(struct file *file,1352const char __user *ubuf,1353size_t count, loff_t *ppos)1354{1355struct ath11k *ar = file->private_data;1356char buf[32] = {0};1357u32 dbr_id, enable;1358int ret;13591360mutex_lock(&ar->conf_mutex);13611362if (ar->state != ATH11K_STATE_ON) {1363ret = -ENETDOWN;1364goto out;1365}13661367ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1368if (ret < 0)1369goto out;13701371buf[ret] = '\0';1372ret = sscanf(buf, "%u %u", &dbr_id, &enable);1373if (ret != 2 || dbr_id > 1 || enable > 1) {1374ret = -EINVAL;1375ath11k_warn(ar->ab, "usage: echo <dbr_id> <val> dbr_id:0-Spectral 1-CFR val:0-disable 1-enable\n");1376goto out;1377}13781379if (enable) {1380ret = ath11k_debugfs_dbr_dbg_init(ar, dbr_id);1381if (ret) {1382ath11k_warn(ar->ab, "db ring module debugfs init failed: %d\n",1383ret);1384goto out;1385}1386} else {1387ath11k_debugfs_dbr_dbg_destroy(ar, dbr_id);1388}13891390ret = count;1391out:1392mutex_unlock(&ar->conf_mutex);1393return ret;1394}13951396static const struct file_operations fops_dbr_debug = {1397.write = ath11k_debugfs_write_enable_dbr_dbg,1398.open = simple_open,1399.owner = THIS_MODULE,1400.llseek = default_llseek,1401};14021403static ssize_t ath11k_write_ps_timekeeper_enable(struct file *file,1404const char __user *user_buf,1405size_t count, loff_t *ppos)1406{1407struct ath11k *ar = file->private_data;1408ssize_t ret;1409u8 ps_timekeeper_enable;14101411if (kstrtou8_from_user(user_buf, count, 0, &ps_timekeeper_enable))1412return -EINVAL;14131414mutex_lock(&ar->conf_mutex);14151416if (ar->state != ATH11K_STATE_ON) {1417ret = -ENETDOWN;1418goto exit;1419}14201421if (!ar->ps_state_enable) {1422ret = -EINVAL;1423goto exit;1424}14251426ar->ps_timekeeper_enable = !!ps_timekeeper_enable;1427ret = count;1428exit:1429mutex_unlock(&ar->conf_mutex);14301431return ret;1432}14331434static ssize_t ath11k_read_ps_timekeeper_enable(struct file *file,1435char __user *user_buf,1436size_t count, loff_t *ppos)1437{1438struct ath11k *ar = file->private_data;1439char buf[32];1440int len;14411442mutex_lock(&ar->conf_mutex);1443len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_timekeeper_enable);1444mutex_unlock(&ar->conf_mutex);14451446return simple_read_from_buffer(user_buf, count, ppos, buf, len);1447}14481449static const struct file_operations fops_ps_timekeeper_enable = {1450.read = ath11k_read_ps_timekeeper_enable,1451.write = ath11k_write_ps_timekeeper_enable,1452.open = simple_open,1453.owner = THIS_MODULE,1454.llseek = default_llseek,1455};14561457static void ath11k_reset_peer_ps_duration(void *data,1458struct ieee80211_sta *sta)1459{1460struct ath11k *ar = data;1461struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;14621463spin_lock_bh(&ar->data_lock);1464arsta->ps_total_duration = 0;1465spin_unlock_bh(&ar->data_lock);1466}14671468static ssize_t ath11k_write_reset_ps_duration(struct file *file,1469const char __user *user_buf,1470size_t count, loff_t *ppos)1471{1472struct ath11k *ar = file->private_data;1473int ret;1474u8 reset_ps_duration;14751476if (kstrtou8_from_user(user_buf, count, 0, &reset_ps_duration))1477return -EINVAL;14781479mutex_lock(&ar->conf_mutex);14801481if (ar->state != ATH11K_STATE_ON) {1482ret = -ENETDOWN;1483goto exit;1484}14851486if (!ar->ps_state_enable) {1487ret = -EINVAL;1488goto exit;1489}14901491ieee80211_iterate_stations_atomic(ar->hw,1492ath11k_reset_peer_ps_duration,1493ar);14941495ret = count;1496exit:1497mutex_unlock(&ar->conf_mutex);1498return ret;1499}15001501static const struct file_operations fops_reset_ps_duration = {1502.write = ath11k_write_reset_ps_duration,1503.open = simple_open,1504.owner = THIS_MODULE,1505.llseek = default_llseek,1506};15071508static void ath11k_peer_ps_state_disable(void *data,1509struct ieee80211_sta *sta)1510{1511struct ath11k *ar = data;1512struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;15131514spin_lock_bh(&ar->data_lock);1515arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;1516arsta->ps_start_time = 0;1517arsta->ps_total_duration = 0;1518spin_unlock_bh(&ar->data_lock);1519}15201521static ssize_t ath11k_write_ps_state_enable(struct file *file,1522const char __user *user_buf,1523size_t count, loff_t *ppos)1524{1525struct ath11k *ar = file->private_data;1526struct ath11k_pdev *pdev = ar->pdev;1527int ret;1528u32 param;1529u8 ps_state_enable;15301531if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))1532return -EINVAL;15331534mutex_lock(&ar->conf_mutex);15351536ps_state_enable = !!ps_state_enable;15371538if (ar->ps_state_enable == ps_state_enable) {1539ret = count;1540goto exit;1541}15421543param = WMI_PDEV_PEER_STA_PS_STATECHG_ENABLE;1544ret = ath11k_wmi_pdev_set_param(ar, param, ps_state_enable, pdev->pdev_id);1545if (ret) {1546ath11k_warn(ar->ab, "failed to enable ps_state_enable: %d\n",1547ret);1548goto exit;1549}1550ar->ps_state_enable = ps_state_enable;15511552if (!ar->ps_state_enable) {1553ar->ps_timekeeper_enable = false;1554ieee80211_iterate_stations_atomic(ar->hw,1555ath11k_peer_ps_state_disable,1556ar);1557}15581559ret = count;15601561exit:1562mutex_unlock(&ar->conf_mutex);15631564return ret;1565}15661567static ssize_t ath11k_read_ps_state_enable(struct file *file,1568char __user *user_buf,1569size_t count, loff_t *ppos)1570{1571struct ath11k *ar = file->private_data;1572char buf[32];1573int len;15741575mutex_lock(&ar->conf_mutex);1576len = scnprintf(buf, sizeof(buf), "%d\n", ar->ps_state_enable);1577mutex_unlock(&ar->conf_mutex);15781579return simple_read_from_buffer(user_buf, count, ppos, buf, len);1580}15811582static const struct file_operations fops_ps_state_enable = {1583.read = ath11k_read_ps_state_enable,1584.write = ath11k_write_ps_state_enable,1585.open = simple_open,1586.owner = THIS_MODULE,1587.llseek = default_llseek,1588};15891590int ath11k_debugfs_register(struct ath11k *ar)1591{1592struct ath11k_base *ab = ar->ab;1593char pdev_name[5];1594char buf[100] = {0};15951596snprintf(pdev_name, sizeof(pdev_name), "%s%d", "mac", ar->pdev_idx);15971598ar->debug.debugfs_pdev = debugfs_create_dir(pdev_name, ab->debugfs_soc);1599if (IS_ERR(ar->debug.debugfs_pdev))1600return PTR_ERR(ar->debug.debugfs_pdev);16011602/* Create a symlink under ieee80211/phy* */1603snprintf(buf, 100, "../../ath11k/%pd2", ar->debug.debugfs_pdev);1604debugfs_create_symlink("ath11k", ar->hw->wiphy->debugfsdir, buf);16051606ath11k_debugfs_htt_stats_init(ar);16071608ath11k_debugfs_fw_stats_init(ar);16091610debugfs_create_file("ext_tx_stats", 0644,1611ar->debug.debugfs_pdev, ar,1612&fops_extd_tx_stats);1613debugfs_create_file("ext_rx_stats", 0644,1614ar->debug.debugfs_pdev, ar,1615&fops_extd_rx_stats);1616debugfs_create_file("pktlog_filter", 0644,1617ar->debug.debugfs_pdev, ar,1618&fops_pktlog_filter);1619debugfs_create_file("fw_dbglog_config", 0600,1620ar->debug.debugfs_pdev, ar,1621&fops_fw_dbglog);16221623if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) {1624debugfs_create_file("dfs_simulate_radar", 0200,1625ar->debug.debugfs_pdev, ar,1626&fops_simulate_radar);1627debugfs_create_bool("dfs_block_radar_events", 0200,1628ar->debug.debugfs_pdev,1629&ar->dfs_block_radar_events);1630}16311632if (ab->hw_params.dbr_debug_support)1633debugfs_create_file("enable_dbr_debug", 0200, ar->debug.debugfs_pdev,1634ar, &fops_dbr_debug);16351636debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_pdev, ar,1637&fops_ps_state_enable);16381639if (test_bit(WMI_TLV_SERVICE_PEER_POWER_SAVE_DURATION_SUPPORT,1640ar->ab->wmi_ab.svc_map)) {1641debugfs_create_file("ps_timekeeper_enable", 0600,1642ar->debug.debugfs_pdev, ar,1643&fops_ps_timekeeper_enable);16441645debugfs_create_file("reset_ps_duration", 0200,1646ar->debug.debugfs_pdev, ar,1647&fops_reset_ps_duration);1648}16491650return 0;1651}16521653void ath11k_debugfs_unregister(struct ath11k *ar)1654{1655struct ath11k_debug_dbr *dbr_debug;1656struct ath11k_dbg_dbr_data *dbr_dbg_data;1657int i;16581659for (i = 0; i < WMI_DIRECT_BUF_MAX; i++) {1660dbr_debug = ar->debug.dbr_debug[i];1661if (!dbr_debug)1662continue;16631664dbr_dbg_data = &dbr_debug->dbr_dbg_data;1665kfree(dbr_dbg_data->entries);1666debugfs_remove_recursive(dbr_debug->dbr_debugfs);1667kfree(dbr_debug);1668ar->debug.dbr_debug[i] = NULL;1669}1670}16711672static ssize_t ath11k_write_twt_add_dialog(struct file *file,1673const char __user *ubuf,1674size_t count, loff_t *ppos)1675{1676struct ath11k_vif *arvif = file->private_data;1677struct wmi_twt_add_dialog_params params = { 0 };1678struct wmi_twt_enable_params twt_params = {0};1679struct ath11k *ar = arvif->ar;1680u8 buf[128] = {0};1681int ret;16821683if (ar->twt_enabled == 0) {1684ath11k_err(ar->ab, "twt support is not enabled\n");1685return -EOPNOTSUPP;1686}16871688ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1689if (ret < 0)1690return ret;16911692buf[ret] = '\0';1693ret = sscanf(buf,1694"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u %u %u %hhu %hhu %hhu %hhu %hhu",1695¶ms.peer_macaddr[0],1696¶ms.peer_macaddr[1],1697¶ms.peer_macaddr[2],1698¶ms.peer_macaddr[3],1699¶ms.peer_macaddr[4],1700¶ms.peer_macaddr[5],1701¶ms.dialog_id,1702¶ms.wake_intvl_us,1703¶ms.wake_intvl_mantis,1704¶ms.wake_dura_us,1705¶ms.sp_offset_us,1706¶ms.twt_cmd,1707¶ms.flag_bcast,1708¶ms.flag_trigger,1709¶ms.flag_flow_type,1710¶ms.flag_protection);1711if (ret != 16)1712return -EINVAL;17131714/* In the case of station vif, TWT is entirely handled by1715* the firmware based on the input parameters in the TWT enable1716* WMI command that is sent to the target during assoc.1717* For manually testing the TWT feature, we need to first disable1718* TWT and send enable command again with TWT input parameter1719* sta_cong_timer_ms set to 0.1720*/1721if (arvif->vif->type == NL80211_IFTYPE_STATION) {1722ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);17231724ath11k_wmi_fill_default_twt_params(&twt_params);1725twt_params.sta_cong_timer_ms = 0;17261727ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);1728}17291730params.vdev_id = arvif->vdev_id;17311732ret = ath11k_wmi_send_twt_add_dialog_cmd(arvif->ar, ¶ms);1733if (ret)1734goto err_twt_add_dialog;17351736return count;17371738err_twt_add_dialog:1739if (arvif->vif->type == NL80211_IFTYPE_STATION) {1740ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);1741ath11k_wmi_fill_default_twt_params(&twt_params);1742ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);1743}17441745return ret;1746}17471748static ssize_t ath11k_write_twt_del_dialog(struct file *file,1749const char __user *ubuf,1750size_t count, loff_t *ppos)1751{1752struct ath11k_vif *arvif = file->private_data;1753struct wmi_twt_del_dialog_params params = { 0 };1754struct wmi_twt_enable_params twt_params = {0};1755struct ath11k *ar = arvif->ar;1756u8 buf[64] = {0};1757int ret;17581759if (ar->twt_enabled == 0) {1760ath11k_err(ar->ab, "twt support is not enabled\n");1761return -EOPNOTSUPP;1762}17631764ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1765if (ret < 0)1766return ret;17671768buf[ret] = '\0';1769ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",1770¶ms.peer_macaddr[0],1771¶ms.peer_macaddr[1],1772¶ms.peer_macaddr[2],1773¶ms.peer_macaddr[3],1774¶ms.peer_macaddr[4],1775¶ms.peer_macaddr[5],1776¶ms.dialog_id);1777if (ret != 7)1778return -EINVAL;17791780params.vdev_id = arvif->vdev_id;17811782ret = ath11k_wmi_send_twt_del_dialog_cmd(arvif->ar, ¶ms);1783if (ret)1784return ret;17851786if (arvif->vif->type == NL80211_IFTYPE_STATION) {1787ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev->pdev_id);1788ath11k_wmi_fill_default_twt_params(&twt_params);1789ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev->pdev_id, &twt_params);1790}17911792return count;1793}17941795static ssize_t ath11k_write_twt_pause_dialog(struct file *file,1796const char __user *ubuf,1797size_t count, loff_t *ppos)1798{1799struct ath11k_vif *arvif = file->private_data;1800struct wmi_twt_pause_dialog_params params = { 0 };1801u8 buf[64] = {0};1802int ret;18031804if (arvif->ar->twt_enabled == 0) {1805ath11k_err(arvif->ar->ab, "twt support is not enabled\n");1806return -EOPNOTSUPP;1807}18081809ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1810if (ret < 0)1811return ret;18121813buf[ret] = '\0';1814ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u",1815¶ms.peer_macaddr[0],1816¶ms.peer_macaddr[1],1817¶ms.peer_macaddr[2],1818¶ms.peer_macaddr[3],1819¶ms.peer_macaddr[4],1820¶ms.peer_macaddr[5],1821¶ms.dialog_id);1822if (ret != 7)1823return -EINVAL;18241825params.vdev_id = arvif->vdev_id;18261827ret = ath11k_wmi_send_twt_pause_dialog_cmd(arvif->ar, ¶ms);1828if (ret)1829return ret;18301831return count;1832}18331834static ssize_t ath11k_write_twt_resume_dialog(struct file *file,1835const char __user *ubuf,1836size_t count, loff_t *ppos)1837{1838struct ath11k_vif *arvif = file->private_data;1839struct wmi_twt_resume_dialog_params params = { 0 };1840u8 buf[64] = {0};1841int ret;18421843if (arvif->ar->twt_enabled == 0) {1844ath11k_err(arvif->ar->ab, "twt support is not enabled\n");1845return -EOPNOTSUPP;1846}18471848ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, ubuf, count);1849if (ret < 0)1850return ret;18511852buf[ret] = '\0';1853ret = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx %u %u %u",1854¶ms.peer_macaddr[0],1855¶ms.peer_macaddr[1],1856¶ms.peer_macaddr[2],1857¶ms.peer_macaddr[3],1858¶ms.peer_macaddr[4],1859¶ms.peer_macaddr[5],1860¶ms.dialog_id,1861¶ms.sp_offset_us,1862¶ms.next_twt_size);1863if (ret != 9)1864return -EINVAL;18651866params.vdev_id = arvif->vdev_id;18671868ret = ath11k_wmi_send_twt_resume_dialog_cmd(arvif->ar, ¶ms);1869if (ret)1870return ret;18711872return count;1873}18741875static const struct file_operations ath11k_fops_twt_add_dialog = {1876.write = ath11k_write_twt_add_dialog,1877.open = simple_open1878};18791880static const struct file_operations ath11k_fops_twt_del_dialog = {1881.write = ath11k_write_twt_del_dialog,1882.open = simple_open1883};18841885static const struct file_operations ath11k_fops_twt_pause_dialog = {1886.write = ath11k_write_twt_pause_dialog,1887.open = simple_open1888};18891890static const struct file_operations ath11k_fops_twt_resume_dialog = {1891.write = ath11k_write_twt_resume_dialog,1892.open = simple_open1893};18941895void ath11k_debugfs_add_interface(struct ath11k_vif *arvif)1896{1897struct ath11k_base *ab = arvif->ar->ab;18981899if (arvif->vif->type != NL80211_IFTYPE_AP &&1900!(arvif->vif->type == NL80211_IFTYPE_STATION &&1901test_bit(WMI_TLV_SERVICE_STA_TWT, ab->wmi_ab.svc_map)))1902return;19031904arvif->debugfs_twt = debugfs_create_dir("twt",1905arvif->vif->debugfs_dir);1906debugfs_create_file("add_dialog", 0200, arvif->debugfs_twt,1907arvif, &ath11k_fops_twt_add_dialog);19081909debugfs_create_file("del_dialog", 0200, arvif->debugfs_twt,1910arvif, &ath11k_fops_twt_del_dialog);19111912debugfs_create_file("pause_dialog", 0200, arvif->debugfs_twt,1913arvif, &ath11k_fops_twt_pause_dialog);19141915debugfs_create_file("resume_dialog", 0200, arvif->debugfs_twt,1916arvif, &ath11k_fops_twt_resume_dialog);1917}19181919void ath11k_debugfs_remove_interface(struct ath11k_vif *arvif)1920{1921if (!arvif->debugfs_twt)1922return;19231924debugfs_remove_recursive(arvif->debugfs_twt);1925arvif->debugfs_twt = NULL;1926}192719281929