Path: blob/main/sys/contrib/dev/athk/ath10k/debug.c
105167 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2005-2011 Atheros Communications Inc.3* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.4* Copyright (c) 2018, The Linux Foundation. All rights reserved.5* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.6* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.7*/89#include <linux/module.h>10#include <linux/debugfs.h>11#include <linux/export.h>12#include <linux/vmalloc.h>13#include <linux/crc32.h>14#include <linux/firmware.h>15#include <linux/kstrtox.h>1617#if defined(__FreeBSD__)18#ifdef CONFIG_ATH10K_DEBUG19#include <sys/sbuf.h>20#endif21#endif2223#include "core.h"24#include "debug.h"25#include "hif.h"26#include "wmi-ops.h"2728/* ms */29#define ATH10K_DEBUG_HTT_STATS_INTERVAL 10003031#define ATH10K_DEBUG_CAL_DATA_LEN 120643233void ath10k_info(struct ath10k *ar, const char *fmt, ...)34{35struct va_format vaf = {36.fmt = fmt,37};38va_list args;3940va_start(args, fmt);41vaf.va = &args;42#if defined(__linux__)43dev_info(ar->dev, "%pV", &vaf);44#elif defined(__FreeBSD__)45{46char *str;47vasprintf(&str, M_KMALLOC, fmt, args);48dev_printk(KERN_DEBUG, ar->dev, "%s", str);49free(str, M_KMALLOC);50}51#endif52trace_ath10k_log_info(ar, &vaf);53va_end(args);54}55EXPORT_SYMBOL(ath10k_info);5657void ath10k_debug_print_hwfw_info(struct ath10k *ar)58{59const struct firmware *firmware;60char fw_features[128] = {};61u32 crc = 0;6263ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));6465#if defined(__linux__)66ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x",67#elif defined(__FreeBSD__)68ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x\n",69#endif70ar->hw_params.name,71ar->target_version,72ar->bus_param.chip_id,73ar->id.subsystem_vendor, ar->id.subsystem_device);7475ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n",76IS_ENABLED(CONFIG_ATH10K_DEBUG),77IS_ENABLED(CONFIG_ATH10K_DEBUGFS),78IS_ENABLED(CONFIG_ATH10K_TRACING),79IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED),80IS_ENABLED(CONFIG_NL80211_TESTMODE));8182firmware = ar->normal_mode_fw.fw_file.firmware;83if (firmware)84crc = crc32_le(0, firmware->data, firmware->size);8586ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n",87ar->hw->wiphy->fw_version,88ar->fw_api,89fw_features,90crc);91}9293void ath10k_debug_print_board_info(struct ath10k *ar)94{95char boardinfo[100];96const struct firmware *board;97u32 crc;9899if (ar->id.bmi_ids_valid)100scnprintf(boardinfo, sizeof(boardinfo), "%d:%d",101ar->id.bmi_chip_id, ar->id.bmi_board_id);102else103scnprintf(boardinfo, sizeof(boardinfo), "N/A");104105board = ar->normal_mode_fw.board;106if (!IS_ERR_OR_NULL(board))107crc = crc32_le(0, board->data, board->size);108else109crc = 0;110111#if defined(__linux__)112ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x",113#elif defined(__FreeBSD__)114ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x\n",115#endif116ar->bd_api,117boardinfo,118crc);119}120121void ath10k_debug_print_boot_info(struct ath10k *ar)122{123ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n",124ar->htt.target_version_major,125ar->htt.target_version_minor,126ar->normal_mode_fw.fw_file.wmi_op_version,127ar->normal_mode_fw.fw_file.htt_op_version,128ath10k_cal_mode_str(ar->cal_mode),129ar->max_num_stations,130test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags),131!test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags));132}133134void ath10k_print_driver_info(struct ath10k *ar)135{136ath10k_debug_print_hwfw_info(ar);137ath10k_debug_print_board_info(ar);138ath10k_debug_print_boot_info(ar);139}140EXPORT_SYMBOL(ath10k_print_driver_info);141142void ath10k_err(struct ath10k *ar, const char *fmt, ...)143{144struct va_format vaf = {145.fmt = fmt,146};147va_list args;148149va_start(args, fmt);150vaf.va = &args;151#if defined(__linux__)152dev_err(ar->dev, "%pV", &vaf);153#elif defined(__FreeBSD__)154{155char *str;156vasprintf(&str, M_KMALLOC, fmt, args);157dev_err(ar->dev, "%s", str);158free(str, M_KMALLOC);159}160#endif161trace_ath10k_log_err(ar, &vaf);162va_end(args);163}164EXPORT_SYMBOL(ath10k_err);165166void ath10k_warn(struct ath10k *ar, const char *fmt, ...)167{168struct va_format vaf = {169.fmt = fmt,170};171va_list args;172173va_start(args, fmt);174vaf.va = &args;175#if defined(__linux__)176dev_warn_ratelimited(ar->dev, "%pV", &vaf);177#elif defined(__FreeBSD__)178{179char *str;180vasprintf(&str, M_KMALLOC, fmt, args);181dev_warn_ratelimited(ar->dev, "%s", str);182free(str, M_KMALLOC);183}184#endif185trace_ath10k_log_warn(ar, &vaf);186187va_end(args);188}189EXPORT_SYMBOL(ath10k_warn);190191#ifdef CONFIG_ATH10K_DEBUGFS192193static ssize_t ath10k_read_wmi_services(struct file *file,194char __user *user_buf,195size_t count, loff_t *ppos)196{197struct ath10k *ar = file->private_data;198char *buf;199size_t len = 0, buf_len = 8192;200const char *name;201ssize_t ret_cnt;202bool enabled;203int i;204205buf = kzalloc(buf_len, GFP_KERNEL);206if (!buf)207return -ENOMEM;208209mutex_lock(&ar->conf_mutex);210211spin_lock_bh(&ar->data_lock);212for (i = 0; i < WMI_SERVICE_MAX; i++) {213enabled = test_bit(i, ar->wmi.svc_map);214name = wmi_service_name(i);215216if (!name) {217if (enabled)218len += scnprintf(buf + len, buf_len - len,219"%-40s %s (bit %d)\n",220"unknown", "enabled", i);221222continue;223}224225len += scnprintf(buf + len, buf_len - len,226"%-40s %s\n",227name, enabled ? "enabled" : "-");228}229spin_unlock_bh(&ar->data_lock);230231ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);232233mutex_unlock(&ar->conf_mutex);234235kfree(buf);236return ret_cnt;237}238239static const struct file_operations fops_wmi_services = {240.read = ath10k_read_wmi_services,241.open = simple_open,242.owner = THIS_MODULE,243.llseek = default_llseek,244};245246static void ath10k_fw_stats_pdevs_free(struct list_head *head)247{248struct ath10k_fw_stats_pdev *i, *tmp;249250list_for_each_entry_safe(i, tmp, head, list) {251list_del(&i->list);252kfree(i);253}254}255256static void ath10k_fw_stats_vdevs_free(struct list_head *head)257{258struct ath10k_fw_stats_vdev *i, *tmp;259260list_for_each_entry_safe(i, tmp, head, list) {261list_del(&i->list);262kfree(i);263}264}265266static void ath10k_fw_stats_peers_free(struct list_head *head)267{268struct ath10k_fw_stats_peer *i, *tmp;269270list_for_each_entry_safe(i, tmp, head, list) {271list_del(&i->list);272kfree(i);273}274}275276static void ath10k_fw_extd_stats_peers_free(struct list_head *head)277{278struct ath10k_fw_extd_stats_peer *i, *tmp;279280list_for_each_entry_safe(i, tmp, head, list) {281list_del(&i->list);282kfree(i);283}284}285286static void ath10k_debug_fw_stats_reset(struct ath10k *ar)287{288spin_lock_bh(&ar->data_lock);289ar->debug.fw_stats_done = false;290ar->debug.fw_stats.extended = false;291ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);292ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);293ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);294ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);295spin_unlock_bh(&ar->data_lock);296}297298void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)299{300struct ath10k_fw_stats stats = {};301bool is_start, is_started, is_end;302size_t num_peers;303size_t num_vdevs;304int ret;305306INIT_LIST_HEAD(&stats.pdevs);307INIT_LIST_HEAD(&stats.vdevs);308INIT_LIST_HEAD(&stats.peers);309INIT_LIST_HEAD(&stats.peers_extd);310311spin_lock_bh(&ar->data_lock);312ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);313if (ret) {314ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);315goto free;316}317318/* Stat data may exceed htc-wmi buffer limit. In such case firmware319* splits the stats data and delivers it in a ping-pong fashion of320* request cmd-update event.321*322* However there is no explicit end-of-data. Instead start-of-data is323* used as an implicit one. This works as follows:324* a) discard stat update events until one with pdev stats is325* delivered - this skips session started at end of (b)326* b) consume stat update events until another one with pdev stats is327* delivered which is treated as end-of-data and is itself discarded328*/329if (ath10k_peer_stats_enabled(ar))330ath10k_sta_update_rx_duration(ar, &stats);331332if (ar->debug.fw_stats_done) {333if (!ath10k_peer_stats_enabled(ar))334ath10k_warn(ar, "received unsolicited stats update event\n");335336goto free;337}338339num_peers = list_count_nodes(&ar->debug.fw_stats.peers);340num_vdevs = list_count_nodes(&ar->debug.fw_stats.vdevs);341is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&342!list_empty(&stats.pdevs));343is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&344!list_empty(&stats.pdevs));345346if (is_start)347list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);348349if (is_end)350ar->debug.fw_stats_done = true;351352if (stats.extended)353ar->debug.fw_stats.extended = true;354355is_started = !list_empty(&ar->debug.fw_stats.pdevs);356357if (is_started && !is_end) {358if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {359/* Although this is unlikely impose a sane limit to360* prevent firmware from DoS-ing the host.361*/362ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);363ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);364ath10k_warn(ar, "dropping fw peer stats\n");365goto free;366}367368if (num_vdevs >= BITS_PER_LONG) {369ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);370ath10k_warn(ar, "dropping fw vdev stats\n");371goto free;372}373374if (!list_empty(&stats.peers))375list_splice_tail_init(&stats.peers_extd,376&ar->debug.fw_stats.peers_extd);377378list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);379list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);380}381382complete(&ar->debug.fw_stats_complete);383384free:385/* In some cases lists have been spliced and cleared. Free up386* resources if that is not the case.387*/388ath10k_fw_stats_pdevs_free(&stats.pdevs);389ath10k_fw_stats_vdevs_free(&stats.vdevs);390ath10k_fw_stats_peers_free(&stats.peers);391ath10k_fw_extd_stats_peers_free(&stats.peers_extd);392393spin_unlock_bh(&ar->data_lock);394}395396int ath10k_debug_fw_stats_request(struct ath10k *ar)397{398unsigned long timeout, time_left;399int ret;400401lockdep_assert_held(&ar->conf_mutex);402403timeout = jiffies + msecs_to_jiffies(1 * HZ);404405ath10k_debug_fw_stats_reset(ar);406407for (;;) {408if (time_after(jiffies, timeout))409return -ETIMEDOUT;410411reinit_completion(&ar->debug.fw_stats_complete);412413ret = ath10k_wmi_request_stats(ar, ar->fw_stats_req_mask);414if (ret) {415ath10k_warn(ar, "could not request stats (%d)\n", ret);416return ret;417}418419time_left =420wait_for_completion_timeout(&ar->debug.fw_stats_complete,4211 * HZ);422if (!time_left)423return -ETIMEDOUT;424425spin_lock_bh(&ar->data_lock);426if (ar->debug.fw_stats_done) {427spin_unlock_bh(&ar->data_lock);428break;429}430spin_unlock_bh(&ar->data_lock);431}432433return 0;434}435436static int ath10k_fw_stats_open(struct inode *inode, struct file *file)437{438struct ath10k *ar = inode->i_private;439void *buf = NULL;440int ret;441442mutex_lock(&ar->conf_mutex);443444if (ar->state != ATH10K_STATE_ON) {445ret = -ENETDOWN;446goto err_unlock;447}448449buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);450if (!buf) {451ret = -ENOMEM;452goto err_unlock;453}454455ret = ath10k_debug_fw_stats_request(ar);456if (ret) {457ath10k_warn(ar, "failed to request fw stats: %d\n", ret);458goto err_free;459}460461ret = ath10k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, buf);462if (ret) {463ath10k_warn(ar, "failed to fill fw stats: %d\n", ret);464goto err_free;465}466467file->private_data = buf;468469mutex_unlock(&ar->conf_mutex);470return 0;471472err_free:473vfree(buf);474475err_unlock:476mutex_unlock(&ar->conf_mutex);477return ret;478}479480static int ath10k_fw_stats_release(struct inode *inode, struct file *file)481{482vfree(file->private_data);483484return 0;485}486487static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,488size_t count, loff_t *ppos)489{490const char *buf = file->private_data;491size_t len = strlen(buf);492493return simple_read_from_buffer(user_buf, count, ppos, buf, len);494}495496static const struct file_operations fops_fw_stats = {497.open = ath10k_fw_stats_open,498.release = ath10k_fw_stats_release,499.read = ath10k_fw_stats_read,500.owner = THIS_MODULE,501.llseek = default_llseek,502};503504static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,505char __user *user_buf,506size_t count, loff_t *ppos)507{508struct ath10k *ar = file->private_data;509int ret;510size_t len = 0, buf_len = 500;511char *buf;512513buf = kmalloc(buf_len, GFP_KERNEL);514if (!buf)515return -ENOMEM;516517spin_lock_bh(&ar->data_lock);518519len += scnprintf(buf + len, buf_len - len,520"fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);521len += scnprintf(buf + len, buf_len - len,522"fw_warm_reset_counter\t\t%d\n",523ar->stats.fw_warm_reset_counter);524len += scnprintf(buf + len, buf_len - len,525"fw_cold_reset_counter\t\t%d\n",526ar->stats.fw_cold_reset_counter);527528spin_unlock_bh(&ar->data_lock);529530ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);531532kfree(buf);533534return ret;535}536537static const struct file_operations fops_fw_reset_stats = {538.open = simple_open,539.read = ath10k_debug_fw_reset_stats_read,540.owner = THIS_MODULE,541.llseek = default_llseek,542};543544/* This is a clean assert crash in firmware. */545static int ath10k_debug_fw_assert(struct ath10k *ar)546{547struct wmi_vdev_install_key_cmd *cmd;548struct sk_buff *skb;549550skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + 16);551if (!skb)552return -ENOMEM;553554cmd = (struct wmi_vdev_install_key_cmd *)skb->data;555memset(cmd, 0, sizeof(*cmd));556557/* big enough number so that firmware asserts */558cmd->vdev_id = __cpu_to_le32(0x7ffe);559560return ath10k_wmi_cmd_send(ar, skb,561ar->wmi.cmd->vdev_install_key_cmdid);562}563564static ssize_t ath10k_read_simulate_fw_crash(struct file *file,565char __user *user_buf,566size_t count, loff_t *ppos)567{568const char buf[] =569"To simulate firmware crash write one of the keywords to this file:\n"570"`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"571"`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"572"`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"573"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";574575return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));576}577578/* Simulate firmware crash:579* 'soft': Call wmi command causing firmware hang. This firmware hang is580* recoverable by warm firmware reset.581* 'hard': Force firmware crash by setting any vdev parameter for not allowed582* vdev id. This is hard firmware crash because it is recoverable only by cold583* firmware reset.584*/585static ssize_t ath10k_write_simulate_fw_crash(struct file *file,586const char __user *user_buf,587size_t count, loff_t *ppos)588{589struct ath10k *ar = file->private_data;590char buf[32] = {};591ssize_t rc;592int ret;593594/* filter partial writes and invalid commands */595if (*ppos != 0 || count >= sizeof(buf) || count == 0)596return -EINVAL;597598rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);599if (rc < 0)600return rc;601602/* drop the possible '\n' from the end */603if (buf[*ppos - 1] == '\n')604buf[*ppos - 1] = '\0';605606mutex_lock(&ar->conf_mutex);607608if (ar->state != ATH10K_STATE_ON &&609ar->state != ATH10K_STATE_RESTARTED) {610ret = -ENETDOWN;611goto exit;612}613614if (!strcmp(buf, "soft")) {615ath10k_info(ar, "simulating soft firmware crash\n");616ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);617} else if (!strcmp(buf, "hard")) {618ath10k_info(ar, "simulating hard firmware crash\n");619/* 0x7fff is vdev id, and it is always out of range for all620* firmware variants in order to force a firmware crash.621*/622ret = ath10k_wmi_vdev_set_param(ar, 0x7fff,623ar->wmi.vdev_param->rts_threshold,6240);625} else if (!strcmp(buf, "assert")) {626ath10k_info(ar, "simulating firmware assert crash\n");627ret = ath10k_debug_fw_assert(ar);628} else if (!strcmp(buf, "hw-restart")) {629ath10k_info(ar, "user requested hw restart\n");630ath10k_core_start_recovery(ar);631ret = 0;632} else {633ret = -EINVAL;634goto exit;635}636637if (ret) {638ath10k_warn(ar, "failed to simulate firmware crash: %d\n", ret);639goto exit;640}641642ret = count;643644exit:645mutex_unlock(&ar->conf_mutex);646return ret;647}648649static const struct file_operations fops_simulate_fw_crash = {650.read = ath10k_read_simulate_fw_crash,651.write = ath10k_write_simulate_fw_crash,652.open = simple_open,653.owner = THIS_MODULE,654.llseek = default_llseek,655};656657static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf,658size_t count, loff_t *ppos)659{660struct ath10k *ar = file->private_data;661size_t len;662char buf[50];663664len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->bus_param.chip_id);665666return simple_read_from_buffer(user_buf, count, ppos, buf, len);667}668669static const struct file_operations fops_chip_id = {670.read = ath10k_read_chip_id,671.open = simple_open,672.owner = THIS_MODULE,673.llseek = default_llseek,674};675676static ssize_t ath10k_reg_addr_read(struct file *file,677char __user *user_buf,678size_t count, loff_t *ppos)679{680struct ath10k *ar = file->private_data;681u8 buf[32];682size_t len = 0;683u32 reg_addr;684685mutex_lock(&ar->conf_mutex);686reg_addr = ar->debug.reg_addr;687mutex_unlock(&ar->conf_mutex);688689len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);690691return simple_read_from_buffer(user_buf, count, ppos, buf, len);692}693694static ssize_t ath10k_reg_addr_write(struct file *file,695const char __user *user_buf,696size_t count, loff_t *ppos)697{698struct ath10k *ar = file->private_data;699u32 reg_addr;700int ret;701702ret = kstrtou32_from_user(user_buf, count, 0, ®_addr);703if (ret)704return ret;705706if (!IS_ALIGNED(reg_addr, 4))707return -EFAULT;708709mutex_lock(&ar->conf_mutex);710ar->debug.reg_addr = reg_addr;711mutex_unlock(&ar->conf_mutex);712713return count;714}715716static const struct file_operations fops_reg_addr = {717.read = ath10k_reg_addr_read,718.write = ath10k_reg_addr_write,719.open = simple_open,720.owner = THIS_MODULE,721.llseek = default_llseek,722};723724static ssize_t ath10k_reg_value_read(struct file *file,725char __user *user_buf,726size_t count, loff_t *ppos)727{728struct ath10k *ar = file->private_data;729u8 buf[48];730size_t len;731u32 reg_addr, reg_val;732int ret;733734mutex_lock(&ar->conf_mutex);735736if (ar->state != ATH10K_STATE_ON &&737ar->state != ATH10K_STATE_UTF) {738ret = -ENETDOWN;739goto exit;740}741742reg_addr = ar->debug.reg_addr;743744reg_val = ath10k_hif_read32(ar, reg_addr);745len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);746747ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);748749exit:750mutex_unlock(&ar->conf_mutex);751752return ret;753}754755static ssize_t ath10k_reg_value_write(struct file *file,756const char __user *user_buf,757size_t count, loff_t *ppos)758{759struct ath10k *ar = file->private_data;760u32 reg_addr, reg_val;761int ret;762763mutex_lock(&ar->conf_mutex);764765if (ar->state != ATH10K_STATE_ON &&766ar->state != ATH10K_STATE_UTF) {767ret = -ENETDOWN;768goto exit;769}770771reg_addr = ar->debug.reg_addr;772773ret = kstrtou32_from_user(user_buf, count, 0, ®_val);774if (ret)775goto exit;776777ath10k_hif_write32(ar, reg_addr, reg_val);778779ret = count;780781exit:782mutex_unlock(&ar->conf_mutex);783784return ret;785}786787static const struct file_operations fops_reg_value = {788.read = ath10k_reg_value_read,789.write = ath10k_reg_value_write,790.open = simple_open,791.owner = THIS_MODULE,792.llseek = default_llseek,793};794795static ssize_t ath10k_mem_value_read(struct file *file,796char __user *user_buf,797size_t count, loff_t *ppos)798{799struct ath10k *ar = file->private_data;800u8 *buf;801int ret;802803if (*ppos < 0)804return -EINVAL;805806if (!count)807return 0;808809mutex_lock(&ar->conf_mutex);810811buf = vmalloc(count);812if (!buf) {813ret = -ENOMEM;814goto exit;815}816817if (ar->state != ATH10K_STATE_ON &&818ar->state != ATH10K_STATE_UTF) {819ret = -ENETDOWN;820goto exit;821}822823ret = ath10k_hif_diag_read(ar, *ppos, buf, count);824if (ret) {825ath10k_warn(ar, "failed to read address 0x%08x via diagnose window from debugfs: %d\n",826(u32)(*ppos), ret);827goto exit;828}829830ret = copy_to_user(user_buf, buf, count);831if (ret) {832ret = -EFAULT;833goto exit;834}835836count -= ret;837*ppos += count;838ret = count;839840exit:841vfree(buf);842mutex_unlock(&ar->conf_mutex);843844return ret;845}846847static ssize_t ath10k_mem_value_write(struct file *file,848const char __user *user_buf,849size_t count, loff_t *ppos)850{851struct ath10k *ar = file->private_data;852u8 *buf;853int ret;854855if (*ppos < 0)856return -EINVAL;857858if (!count)859return 0;860861mutex_lock(&ar->conf_mutex);862863buf = vmalloc(count);864if (!buf) {865ret = -ENOMEM;866goto exit;867}868869if (ar->state != ATH10K_STATE_ON &&870ar->state != ATH10K_STATE_UTF) {871ret = -ENETDOWN;872goto exit;873}874875ret = copy_from_user(buf, user_buf, count);876if (ret) {877ret = -EFAULT;878goto exit;879}880881ret = ath10k_hif_diag_write(ar, *ppos, buf, count);882if (ret) {883ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",884(u32)(*ppos), ret);885goto exit;886}887888*ppos += count;889ret = count;890891exit:892vfree(buf);893mutex_unlock(&ar->conf_mutex);894895return ret;896}897898static const struct file_operations fops_mem_value = {899.read = ath10k_mem_value_read,900.write = ath10k_mem_value_write,901.open = simple_open,902.owner = THIS_MODULE,903.llseek = default_llseek,904};905906static int ath10k_debug_htt_stats_req(struct ath10k *ar)907{908u64 cookie;909int ret;910911lockdep_assert_held(&ar->conf_mutex);912913if (ar->debug.htt_stats_mask == 0)914/* htt stats are disabled */915return 0;916917if (ar->state != ATH10K_STATE_ON)918return 0;919920cookie = get_jiffies_64();921922ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,923ar->debug.reset_htt_stats, cookie);924if (ret) {925ath10k_warn(ar, "failed to send htt stats request: %d\n", ret);926return ret;927}928929queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,930msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));931932return 0;933}934935static void ath10k_debug_htt_stats_dwork(struct work_struct *work)936{937struct ath10k *ar = container_of(work, struct ath10k,938debug.htt_stats_dwork.work);939940mutex_lock(&ar->conf_mutex);941942ath10k_debug_htt_stats_req(ar);943944mutex_unlock(&ar->conf_mutex);945}946947static ssize_t ath10k_read_htt_stats_mask(struct file *file,948char __user *user_buf,949size_t count, loff_t *ppos)950{951struct ath10k *ar = file->private_data;952char buf[32];953size_t len;954955len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);956957return simple_read_from_buffer(user_buf, count, ppos, buf, len);958}959960static ssize_t ath10k_write_htt_stats_mask(struct file *file,961const char __user *user_buf,962size_t count, loff_t *ppos)963{964struct ath10k *ar = file->private_data;965unsigned long mask;966int ret;967968ret = kstrtoul_from_user(user_buf, count, 0, &mask);969if (ret)970return ret;971972/* max 17 bit masks (for now) */973if (mask > HTT_STATS_BIT_MASK)974return -E2BIG;975976mutex_lock(&ar->conf_mutex);977978ar->debug.htt_stats_mask = mask;979980ret = ath10k_debug_htt_stats_req(ar);981if (ret)982goto out;983984ret = count;985986out:987mutex_unlock(&ar->conf_mutex);988989return ret;990}991992static const struct file_operations fops_htt_stats_mask = {993.read = ath10k_read_htt_stats_mask,994.write = ath10k_write_htt_stats_mask,995.open = simple_open,996.owner = THIS_MODULE,997.llseek = default_llseek,998};9991000static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,1001char __user *user_buf,1002size_t count, loff_t *ppos)1003{1004struct ath10k *ar = file->private_data;1005char buf[64];1006u8 amsdu, ampdu;1007size_t len;10081009mutex_lock(&ar->conf_mutex);10101011amsdu = ar->htt.max_num_amsdu;1012ampdu = ar->htt.max_num_ampdu;1013mutex_unlock(&ar->conf_mutex);10141015len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);10161017return simple_read_from_buffer(user_buf, count, ppos, buf, len);1018}10191020static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,1021const char __user *user_buf,1022size_t count, loff_t *ppos)1023{1024struct ath10k *ar = file->private_data;1025int res;1026char buf[64] = {};1027unsigned int amsdu, ampdu;10281029res = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,1030user_buf, count);1031if (res <= 0)1032return res;10331034res = sscanf(buf, "%u %u", &amsdu, &du);10351036if (res != 2)1037return -EINVAL;10381039mutex_lock(&ar->conf_mutex);10401041res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);1042if (res)1043goto out;10441045res = count;1046ar->htt.max_num_amsdu = amsdu;1047ar->htt.max_num_ampdu = ampdu;10481049out:1050mutex_unlock(&ar->conf_mutex);1051return res;1052}10531054static const struct file_operations fops_htt_max_amsdu_ampdu = {1055.read = ath10k_read_htt_max_amsdu_ampdu,1056.write = ath10k_write_htt_max_amsdu_ampdu,1057.open = simple_open,1058.owner = THIS_MODULE,1059.llseek = default_llseek,1060};10611062static ssize_t ath10k_read_fw_dbglog(struct file *file,1063char __user *user_buf,1064size_t count, loff_t *ppos)1065{1066struct ath10k *ar = file->private_data;1067size_t len;1068char buf[96];10691070len = scnprintf(buf, sizeof(buf), "0x%16llx %u\n",1071ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);10721073return simple_read_from_buffer(user_buf, count, ppos, buf, len);1074}10751076static ssize_t ath10k_write_fw_dbglog(struct file *file,1077const char __user *user_buf,1078size_t count, loff_t *ppos)1079{1080struct ath10k *ar = file->private_data;1081int ret;1082char buf[96] = {};1083unsigned int log_level;1084u64 mask;10851086ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,1087user_buf, count);1088if (ret <= 0)1089return ret;10901091ret = sscanf(buf, "%llx %u", &mask, &log_level);10921093if (!ret)1094return -EINVAL;10951096if (ret == 1)1097/* default if user did not specify */1098log_level = ATH10K_DBGLOG_LEVEL_WARN;10991100mutex_lock(&ar->conf_mutex);11011102ar->debug.fw_dbglog_mask = mask;1103ar->debug.fw_dbglog_level = log_level;11041105if (ar->state == ATH10K_STATE_ON) {1106ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,1107ar->debug.fw_dbglog_level);1108if (ret) {1109ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n",1110ret);1111goto exit;1112}1113}11141115ret = count;11161117exit:1118mutex_unlock(&ar->conf_mutex);11191120return ret;1121}11221123/* TODO: Would be nice to always support ethtool stats, would need to1124* move the stats storage out of ath10k_debug, or always have ath10k_debug1125* struct available..1126*/11271128/* This generally corresponds to the debugfs fw_stats file */1129static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = {1130"tx_pkts_nic",1131"tx_bytes_nic",1132"rx_pkts_nic",1133"rx_bytes_nic",1134"d_noise_floor",1135"d_cycle_count",1136"d_phy_error",1137"d_rts_bad",1138"d_rts_good",1139"d_tx_power", /* in .5 dbM I think */1140"d_rx_crc_err", /* fcs_bad */1141"d_rx_crc_err_drop", /* frame with FCS error, dropped late in kernel */1142"d_no_beacon",1143"d_tx_mpdus_queued",1144"d_tx_msdu_queued",1145"d_tx_msdu_dropped",1146"d_local_enqued",1147"d_local_freed",1148"d_tx_ppdu_hw_queued",1149"d_tx_ppdu_reaped",1150"d_tx_fifo_underrun",1151"d_tx_ppdu_abort",1152"d_tx_mpdu_requeued",1153"d_tx_excessive_retries",1154"d_tx_hw_rate",1155"d_tx_dropped_sw_retries",1156"d_tx_illegal_rate",1157"d_tx_continuous_xretries",1158"d_tx_timeout",1159"d_tx_mpdu_txop_limit",1160"d_pdev_resets",1161"d_rx_mid_ppdu_route_change",1162"d_rx_status",1163"d_rx_extra_frags_ring0",1164"d_rx_extra_frags_ring1",1165"d_rx_extra_frags_ring2",1166"d_rx_extra_frags_ring3",1167"d_rx_msdu_htt",1168"d_rx_mpdu_htt",1169"d_rx_msdu_stack",1170"d_rx_mpdu_stack",1171"d_rx_phy_err",1172"d_rx_phy_err_drops",1173"d_rx_mpdu_errors", /* FCS, MIC, ENC */1174"d_fw_crash_count",1175"d_fw_warm_reset_count",1176"d_fw_cold_reset_count",1177};11781179#define ATH10K_SSTATS_LEN ARRAY_SIZE(ath10k_gstrings_stats)11801181void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,1182struct ieee80211_vif *vif,1183u32 sset, u8 *data)1184{1185if (sset == ETH_SS_STATS)1186memcpy(data, ath10k_gstrings_stats,1187sizeof(ath10k_gstrings_stats));1188}11891190int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,1191struct ieee80211_vif *vif, int sset)1192{1193if (sset == ETH_SS_STATS)1194return ATH10K_SSTATS_LEN;11951196return 0;1197}11981199void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,1200struct ieee80211_vif *vif,1201struct ethtool_stats *stats, u64 *data)1202{1203struct ath10k *ar = hw->priv;1204static const struct ath10k_fw_stats_pdev zero_stats = {};1205const struct ath10k_fw_stats_pdev *pdev_stats;1206int i = 0, ret;12071208mutex_lock(&ar->conf_mutex);12091210if (ar->state == ATH10K_STATE_ON) {1211ret = ath10k_debug_fw_stats_request(ar);1212if (ret) {1213/* just print a warning and try to use older results */1214ath10k_warn(ar,1215"failed to get fw stats for ethtool: %d\n",1216ret);1217}1218}12191220pdev_stats = list_first_entry_or_null(&ar->debug.fw_stats.pdevs,1221struct ath10k_fw_stats_pdev,1222list);1223if (!pdev_stats) {1224/* no results available so just return zeroes */1225pdev_stats = &zero_stats;1226}12271228spin_lock_bh(&ar->data_lock);12291230data[i++] = pdev_stats->hw_reaped; /* ppdu reaped */1231data[i++] = 0; /* tx bytes */1232data[i++] = pdev_stats->htt_mpdus;1233data[i++] = 0; /* rx bytes */1234data[i++] = pdev_stats->ch_noise_floor;1235data[i++] = pdev_stats->cycle_count;1236data[i++] = pdev_stats->phy_err_count;1237data[i++] = pdev_stats->rts_bad;1238data[i++] = pdev_stats->rts_good;1239data[i++] = pdev_stats->chan_tx_power;1240data[i++] = pdev_stats->fcs_bad;1241data[i++] = ar->stats.rx_crc_err_drop;1242data[i++] = pdev_stats->no_beacons;1243data[i++] = pdev_stats->mpdu_enqued;1244data[i++] = pdev_stats->msdu_enqued;1245data[i++] = pdev_stats->wmm_drop;1246data[i++] = pdev_stats->local_enqued;1247data[i++] = pdev_stats->local_freed;1248data[i++] = pdev_stats->hw_queued;1249data[i++] = pdev_stats->hw_reaped;1250data[i++] = pdev_stats->underrun;1251data[i++] = pdev_stats->tx_abort;1252data[i++] = pdev_stats->mpdus_requeued;1253data[i++] = pdev_stats->tx_ko;1254data[i++] = pdev_stats->data_rc;1255data[i++] = pdev_stats->sw_retry_failure;1256data[i++] = pdev_stats->illgl_rate_phy_err;1257data[i++] = pdev_stats->pdev_cont_xretry;1258data[i++] = pdev_stats->pdev_tx_timeout;1259data[i++] = pdev_stats->txop_ovf;1260data[i++] = pdev_stats->pdev_resets;1261data[i++] = pdev_stats->mid_ppdu_route_change;1262data[i++] = pdev_stats->status_rcvd;1263data[i++] = pdev_stats->r0_frags;1264data[i++] = pdev_stats->r1_frags;1265data[i++] = pdev_stats->r2_frags;1266data[i++] = pdev_stats->r3_frags;1267data[i++] = pdev_stats->htt_msdus;1268data[i++] = pdev_stats->htt_mpdus;1269data[i++] = pdev_stats->loc_msdus;1270data[i++] = pdev_stats->loc_mpdus;1271data[i++] = pdev_stats->phy_errs;1272data[i++] = pdev_stats->phy_err_drop;1273data[i++] = pdev_stats->mpdu_errs;1274data[i++] = ar->stats.fw_crash_counter;1275data[i++] = ar->stats.fw_warm_reset_counter;1276data[i++] = ar->stats.fw_cold_reset_counter;12771278spin_unlock_bh(&ar->data_lock);12791280mutex_unlock(&ar->conf_mutex);12811282WARN_ON(i != ATH10K_SSTATS_LEN);1283}12841285static const struct file_operations fops_fw_dbglog = {1286.read = ath10k_read_fw_dbglog,1287.write = ath10k_write_fw_dbglog,1288.open = simple_open,1289.owner = THIS_MODULE,1290.llseek = default_llseek,1291};12921293static int ath10k_debug_cal_data_fetch(struct ath10k *ar)1294{1295u32 hi_addr;1296__le32 addr;1297int ret;12981299lockdep_assert_held(&ar->conf_mutex);13001301if (WARN_ON(ar->hw_params.cal_data_len > ATH10K_DEBUG_CAL_DATA_LEN))1302return -EINVAL;13031304if (ar->hw_params.cal_data_len == 0)1305return -EOPNOTSUPP;13061307hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));13081309ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));1310if (ret) {1311ath10k_warn(ar, "failed to read hi_board_data address: %d\n",1312ret);1313return ret;1314}13151316ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), ar->debug.cal_data,1317ar->hw_params.cal_data_len);1318if (ret) {1319ath10k_warn(ar, "failed to read calibration data: %d\n", ret);1320return ret;1321}13221323return 0;1324}13251326static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)1327{1328struct ath10k *ar = inode->i_private;13291330mutex_lock(&ar->conf_mutex);13311332if (ar->state == ATH10K_STATE_ON ||1333ar->state == ATH10K_STATE_UTF) {1334ath10k_debug_cal_data_fetch(ar);1335}13361337file->private_data = ar;1338mutex_unlock(&ar->conf_mutex);13391340return 0;1341}13421343static ssize_t ath10k_debug_cal_data_read(struct file *file,1344char __user *user_buf,1345size_t count, loff_t *ppos)1346{1347struct ath10k *ar = file->private_data;13481349mutex_lock(&ar->conf_mutex);13501351count = simple_read_from_buffer(user_buf, count, ppos,1352ar->debug.cal_data,1353ar->hw_params.cal_data_len);13541355mutex_unlock(&ar->conf_mutex);13561357return count;1358}13591360static ssize_t ath10k_write_ani_enable(struct file *file,1361const char __user *user_buf,1362size_t count, loff_t *ppos)1363{1364struct ath10k *ar = file->private_data;1365int ret;1366u8 enable;13671368if (kstrtou8_from_user(user_buf, count, 0, &enable))1369return -EINVAL;13701371mutex_lock(&ar->conf_mutex);13721373if (ar->ani_enabled == enable) {1374ret = count;1375goto exit;1376}13771378ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->ani_enable,1379enable);1380if (ret) {1381ath10k_warn(ar, "ani_enable failed from debugfs: %d\n", ret);1382goto exit;1383}1384ar->ani_enabled = enable;13851386ret = count;13871388exit:1389mutex_unlock(&ar->conf_mutex);13901391return ret;1392}13931394static ssize_t ath10k_read_ani_enable(struct file *file, char __user *user_buf,1395size_t count, loff_t *ppos)1396{1397struct ath10k *ar = file->private_data;1398size_t len;1399char buf[32];14001401len = scnprintf(buf, sizeof(buf), "%d\n", ar->ani_enabled);14021403return simple_read_from_buffer(user_buf, count, ppos, buf, len);1404}14051406static const struct file_operations fops_ani_enable = {1407.read = ath10k_read_ani_enable,1408.write = ath10k_write_ani_enable,1409.open = simple_open,1410.owner = THIS_MODULE,1411.llseek = default_llseek,1412};14131414static const struct file_operations fops_cal_data = {1415.open = ath10k_debug_cal_data_open,1416.read = ath10k_debug_cal_data_read,1417.owner = THIS_MODULE,1418.llseek = default_llseek,1419};14201421static ssize_t ath10k_read_nf_cal_period(struct file *file,1422char __user *user_buf,1423size_t count, loff_t *ppos)1424{1425struct ath10k *ar = file->private_data;1426size_t len;1427char buf[32];14281429len = scnprintf(buf, sizeof(buf), "%d\n", ar->debug.nf_cal_period);14301431return simple_read_from_buffer(user_buf, count, ppos, buf, len);1432}14331434static ssize_t ath10k_write_nf_cal_period(struct file *file,1435const char __user *user_buf,1436size_t count, loff_t *ppos)1437{1438struct ath10k *ar = file->private_data;1439unsigned long period;1440int ret;14411442ret = kstrtoul_from_user(user_buf, count, 0, &period);1443if (ret)1444return ret;14451446if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX)1447return -EINVAL;14481449/* there's no way to switch back to the firmware default */1450if (period == 0)1451return -EINVAL;14521453mutex_lock(&ar->conf_mutex);14541455ar->debug.nf_cal_period = period;14561457if (ar->state != ATH10K_STATE_ON) {1458/* firmware is not running, nothing else to do */1459ret = count;1460goto exit;1461}14621463ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period,1464ar->debug.nf_cal_period);1465if (ret) {1466ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n",1467ret);1468goto exit;1469}14701471ret = count;14721473exit:1474mutex_unlock(&ar->conf_mutex);14751476return ret;1477}14781479static const struct file_operations fops_nf_cal_period = {1480.read = ath10k_read_nf_cal_period,1481.write = ath10k_write_nf_cal_period,1482.open = simple_open,1483.owner = THIS_MODULE,1484.llseek = default_llseek,1485};14861487#define ATH10K_TPC_CONFIG_BUF_SIZE (1024 * 1024)14881489static int ath10k_debug_tpc_stats_request(struct ath10k *ar)1490{1491int ret;1492unsigned long time_left;14931494lockdep_assert_held(&ar->conf_mutex);14951496reinit_completion(&ar->debug.tpc_complete);14971498ret = ath10k_wmi_pdev_get_tpc_config(ar, WMI_TPC_CONFIG_PARAM);1499if (ret) {1500ath10k_warn(ar, "failed to request tpc config: %d\n", ret);1501return ret;1502}15031504time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,15051 * HZ);1506if (time_left == 0)1507return -ETIMEDOUT;15081509return 0;1510}15111512void ath10k_debug_tpc_stats_process(struct ath10k *ar,1513struct ath10k_tpc_stats *tpc_stats)1514{1515spin_lock_bh(&ar->data_lock);15161517kfree(ar->debug.tpc_stats);1518ar->debug.tpc_stats = tpc_stats;1519complete(&ar->debug.tpc_complete);15201521spin_unlock_bh(&ar->data_lock);1522}15231524void1525ath10k_debug_tpc_stats_final_process(struct ath10k *ar,1526struct ath10k_tpc_stats_final *tpc_stats)1527{1528spin_lock_bh(&ar->data_lock);15291530kfree(ar->debug.tpc_stats_final);1531ar->debug.tpc_stats_final = tpc_stats;1532complete(&ar->debug.tpc_complete);15331534spin_unlock_bh(&ar->data_lock);1535}15361537static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats,1538unsigned int j, char *buf, size_t *len)1539{1540int i;1541size_t buf_len;1542static const char table_str[][5] = { "CDD",1543"STBC",1544"TXBF" };1545static const char pream_str[][6] = { "CCK",1546"OFDM",1547"HT20",1548"HT40",1549"VHT20",1550"VHT40",1551"VHT80",1552"HTCUP" };15531554buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;1555*len += scnprintf(buf + *len, buf_len - *len,1556"********************************\n");1557*len += scnprintf(buf + *len, buf_len - *len,1558"******************* %s POWER TABLE ****************\n",1559table_str[j]);1560*len += scnprintf(buf + *len, buf_len - *len,1561"********************************\n");1562*len += scnprintf(buf + *len, buf_len - *len,1563"No. Preamble Rate_code ");15641565for (i = 0; i < tpc_stats->num_tx_chain; i++)1566*len += scnprintf(buf + *len, buf_len - *len,1567"tpc_value%d ", i);15681569*len += scnprintf(buf + *len, buf_len - *len, "\n");15701571for (i = 0; i < tpc_stats->rate_max; i++) {1572*len += scnprintf(buf + *len, buf_len - *len,1573"%8d %s 0x%2x %s\n", i,1574pream_str[tpc_stats->tpc_table[j].pream_idx[i]],1575tpc_stats->tpc_table[j].rate_code[i],1576tpc_stats->tpc_table[j].tpc_value[i]);1577}15781579*len += scnprintf(buf + *len, buf_len - *len,1580"***********************************\n");1581}15821583static void ath10k_tpc_stats_fill(struct ath10k *ar,1584struct ath10k_tpc_stats *tpc_stats,1585char *buf)1586{1587int j;1588size_t len, buf_len;15891590len = 0;1591buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;15921593spin_lock_bh(&ar->data_lock);15941595if (!tpc_stats) {1596ath10k_warn(ar, "failed to get tpc stats\n");1597goto unlock;1598}15991600len += scnprintf(buf + len, buf_len - len, "\n");1601len += scnprintf(buf + len, buf_len - len,1602"*************************************\n");1603len += scnprintf(buf + len, buf_len - len,1604"TPC config for channel %4d mode %d\n",1605tpc_stats->chan_freq,1606tpc_stats->phy_mode);1607len += scnprintf(buf + len, buf_len - len,1608"*************************************\n");1609len += scnprintf(buf + len, buf_len - len,1610"CTL = 0x%2x Reg. Domain = %2d\n",1611tpc_stats->ctl,1612tpc_stats->reg_domain);1613len += scnprintf(buf + len, buf_len - len,1614"Antenna Gain = %2d Reg. Max Antenna Gain = %2d\n",1615tpc_stats->twice_antenna_gain,1616tpc_stats->twice_antenna_reduction);1617len += scnprintf(buf + len, buf_len - len,1618"Power Limit = %2d Reg. Max Power = %2d\n",1619tpc_stats->power_limit,1620tpc_stats->twice_max_rd_power / 2);1621len += scnprintf(buf + len, buf_len - len,1622"Num tx chains = %2d Num supported rates = %2d\n",1623tpc_stats->num_tx_chain,1624tpc_stats->rate_max);16251626for (j = 0; j < WMI_TPC_FLAG; j++) {1627switch (j) {1628case WMI_TPC_TABLE_TYPE_CDD:1629if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {1630len += scnprintf(buf + len, buf_len - len,1631"CDD not supported\n");1632break;1633}16341635ath10k_tpc_stats_print(tpc_stats, j, buf, &len);1636break;1637case WMI_TPC_TABLE_TYPE_STBC:1638if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {1639len += scnprintf(buf + len, buf_len - len,1640"STBC not supported\n");1641break;1642}16431644ath10k_tpc_stats_print(tpc_stats, j, buf, &len);1645break;1646case WMI_TPC_TABLE_TYPE_TXBF:1647if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {1648len += scnprintf(buf + len, buf_len - len,1649"TXBF not supported\n***************************\n");1650break;1651}16521653ath10k_tpc_stats_print(tpc_stats, j, buf, &len);1654break;1655default:1656len += scnprintf(buf + len, buf_len - len,1657"Invalid Type\n");1658break;1659}1660}16611662unlock:1663spin_unlock_bh(&ar->data_lock);16641665if (len >= buf_len)1666buf[len - 1] = 0;1667else1668buf[len] = 0;1669}16701671static int ath10k_tpc_stats_open(struct inode *inode, struct file *file)1672{1673struct ath10k *ar = inode->i_private;1674void *buf = NULL;1675int ret;16761677mutex_lock(&ar->conf_mutex);16781679if (ar->state != ATH10K_STATE_ON) {1680ret = -ENETDOWN;1681goto err_unlock;1682}16831684buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);1685if (!buf) {1686ret = -ENOMEM;1687goto err_unlock;1688}16891690ret = ath10k_debug_tpc_stats_request(ar);1691if (ret) {1692ath10k_warn(ar, "failed to request tpc config stats: %d\n",1693ret);1694goto err_free;1695}16961697ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);1698file->private_data = buf;16991700mutex_unlock(&ar->conf_mutex);1701return 0;17021703err_free:1704vfree(buf);17051706err_unlock:1707mutex_unlock(&ar->conf_mutex);1708return ret;1709}17101711static int ath10k_tpc_stats_release(struct inode *inode, struct file *file)1712{1713vfree(file->private_data);17141715return 0;1716}17171718static ssize_t ath10k_tpc_stats_read(struct file *file, char __user *user_buf,1719size_t count, loff_t *ppos)1720{1721const char *buf = file->private_data;1722size_t len = strlen(buf);17231724return simple_read_from_buffer(user_buf, count, ppos, buf, len);1725}17261727static const struct file_operations fops_tpc_stats = {1728.open = ath10k_tpc_stats_open,1729.release = ath10k_tpc_stats_release,1730.read = ath10k_tpc_stats_read,1731.owner = THIS_MODULE,1732.llseek = default_llseek,1733};17341735int ath10k_debug_start(struct ath10k *ar)1736{1737int ret;17381739lockdep_assert_held(&ar->conf_mutex);17401741ret = ath10k_debug_htt_stats_req(ar);1742if (ret)1743/* continue normally anyway, this isn't serious */1744ath10k_warn(ar, "failed to start htt stats workqueue: %d\n",1745ret);17461747if (ar->debug.fw_dbglog_mask) {1748ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,1749ATH10K_DBGLOG_LEVEL_WARN);1750if (ret)1751/* not serious */1752ath10k_warn(ar, "failed to enable dbglog during start: %d",1753ret);1754}17551756if (ar->pktlog_filter) {1757ret = ath10k_wmi_pdev_pktlog_enable(ar,1758ar->pktlog_filter);1759if (ret)1760/* not serious */1761ath10k_warn(ar,1762"failed to enable pktlog filter %x: %d\n",1763ar->pktlog_filter, ret);1764} else {1765ret = ath10k_wmi_pdev_pktlog_disable(ar);1766if (ret)1767/* not serious */1768ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);1769}17701771if (ar->debug.nf_cal_period &&1772!test_bit(ATH10K_FW_FEATURE_NON_BMI,1773ar->normal_mode_fw.fw_file.fw_features)) {1774ret = ath10k_wmi_pdev_set_param(ar,1775ar->wmi.pdev_param->cal_period,1776ar->debug.nf_cal_period);1777if (ret)1778/* not serious */1779ath10k_warn(ar, "cal period cfg failed from debug start: %d\n",1780ret);1781}17821783return ret;1784}17851786void ath10k_debug_stop(struct ath10k *ar)1787{1788lockdep_assert_held(&ar->conf_mutex);17891790if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,1791ar->normal_mode_fw.fw_file.fw_features))1792ath10k_debug_cal_data_fetch(ar);17931794/* Must not use _sync to avoid deadlock, we do that in1795* ath10k_debug_destroy(). The check for htt_stats_mask is to avoid1796* warning from timer_delete().1797*/1798if (ar->debug.htt_stats_mask != 0)1799cancel_delayed_work(&ar->debug.htt_stats_dwork);18001801ath10k_wmi_pdev_pktlog_disable(ar);1802}18031804static ssize_t ath10k_write_simulate_radar(struct file *file,1805const char __user *user_buf,1806size_t count, loff_t *ppos)1807{1808struct ath10k *ar = file->private_data;1809struct ath10k_vif *arvif;18101811/* Just check for the first vif alone, as all the vifs will be1812* sharing the same channel and if the channel is disabled, all the1813* vifs will share the same 'is_started' state.1814*/1815arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);1816if (!arvif->is_started)1817return -EINVAL;18181819ieee80211_radar_detected(ar->hw, NULL);18201821return count;1822}18231824static const struct file_operations fops_simulate_radar = {1825.write = ath10k_write_simulate_radar,1826.open = simple_open,1827.owner = THIS_MODULE,1828.llseek = default_llseek,1829};18301831#define ATH10K_DFS_STAT(s, p) (\1832len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \1833ar->debug.dfs_stats.p))18341835#define ATH10K_DFS_POOL_STAT(s, p) (\1836len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \1837ar->debug.dfs_pool_stats.p))18381839static ssize_t ath10k_read_dfs_stats(struct file *file, char __user *user_buf,1840size_t count, loff_t *ppos)1841{1842int retval = 0, len = 0;1843const int size = 8000;1844struct ath10k *ar = file->private_data;1845char *buf;18461847buf = kzalloc(size, GFP_KERNEL);1848if (buf == NULL)1849return -ENOMEM;18501851if (!ar->dfs_detector) {1852len += scnprintf(buf + len, size - len, "DFS not enabled\n");1853goto exit;1854}18551856ar->debug.dfs_pool_stats =1857ar->dfs_detector->get_stats(ar->dfs_detector);18581859len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");18601861ATH10K_DFS_STAT("reported phy errors", phy_errors);1862ATH10K_DFS_STAT("pulse events reported", pulses_total);1863ATH10K_DFS_STAT("DFS pulses detected", pulses_detected);1864ATH10K_DFS_STAT("DFS pulses discarded", pulses_discarded);1865ATH10K_DFS_STAT("Radars detected", radar_detected);18661867len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");1868ATH10K_DFS_POOL_STAT("Pool references", pool_reference);1869ATH10K_DFS_POOL_STAT("Pulses allocated", pulse_allocated);1870ATH10K_DFS_POOL_STAT("Pulses alloc error", pulse_alloc_error);1871ATH10K_DFS_POOL_STAT("Pulses in use", pulse_used);1872ATH10K_DFS_POOL_STAT("Seqs. allocated", pseq_allocated);1873ATH10K_DFS_POOL_STAT("Seqs. alloc error", pseq_alloc_error);1874ATH10K_DFS_POOL_STAT("Seqs. in use", pseq_used);18751876exit:1877if (len > size)1878len = size;18791880retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);1881kfree(buf);18821883return retval;1884}18851886static const struct file_operations fops_dfs_stats = {1887.read = ath10k_read_dfs_stats,1888.open = simple_open,1889.owner = THIS_MODULE,1890.llseek = default_llseek,1891};18921893static ssize_t ath10k_write_pktlog_filter(struct file *file,1894const char __user *ubuf,1895size_t count, loff_t *ppos)1896{1897struct ath10k *ar = file->private_data;1898u32 filter;1899int ret;19001901if (kstrtouint_from_user(ubuf, count, 0, &filter))1902return -EINVAL;19031904mutex_lock(&ar->conf_mutex);19051906if (ar->state != ATH10K_STATE_ON) {1907ar->pktlog_filter = filter;1908ret = count;1909goto out;1910}19111912if (filter == ar->pktlog_filter) {1913ret = count;1914goto out;1915}19161917if (filter) {1918ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);1919if (ret) {1920ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",1921ar->pktlog_filter, ret);1922goto out;1923}1924} else {1925ret = ath10k_wmi_pdev_pktlog_disable(ar);1926if (ret) {1927ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);1928goto out;1929}1930}19311932ar->pktlog_filter = filter;1933ret = count;19341935out:1936mutex_unlock(&ar->conf_mutex);1937return ret;1938}19391940static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,1941size_t count, loff_t *ppos)1942{1943char buf[32];1944struct ath10k *ar = file->private_data;1945int len = 0;19461947mutex_lock(&ar->conf_mutex);1948len = scnprintf(buf, sizeof(buf) - len, "%08x\n",1949ar->pktlog_filter);1950mutex_unlock(&ar->conf_mutex);19511952return simple_read_from_buffer(ubuf, count, ppos, buf, len);1953}19541955static const struct file_operations fops_pktlog_filter = {1956.read = ath10k_read_pktlog_filter,1957.write = ath10k_write_pktlog_filter,1958.open = simple_open1959};19601961static ssize_t ath10k_write_quiet_period(struct file *file,1962const char __user *ubuf,1963size_t count, loff_t *ppos)1964{1965struct ath10k *ar = file->private_data;1966u32 period;19671968if (kstrtouint_from_user(ubuf, count, 0, &period))1969return -EINVAL;19701971if (period < ATH10K_QUIET_PERIOD_MIN) {1972ath10k_warn(ar, "Quiet period %u can not be lesser than 25ms\n",1973period);1974return -EINVAL;1975}1976mutex_lock(&ar->conf_mutex);1977ar->thermal.quiet_period = period;1978ath10k_thermal_set_throttling(ar);1979mutex_unlock(&ar->conf_mutex);19801981return count;1982}19831984static ssize_t ath10k_read_quiet_period(struct file *file, char __user *ubuf,1985size_t count, loff_t *ppos)1986{1987char buf[32];1988struct ath10k *ar = file->private_data;1989int len = 0;19901991mutex_lock(&ar->conf_mutex);1992len = scnprintf(buf, sizeof(buf) - len, "%d\n",1993ar->thermal.quiet_period);1994mutex_unlock(&ar->conf_mutex);19951996return simple_read_from_buffer(ubuf, count, ppos, buf, len);1997}19981999static const struct file_operations fops_quiet_period = {2000.read = ath10k_read_quiet_period,2001.write = ath10k_write_quiet_period,2002.open = simple_open2003};20042005static ssize_t ath10k_write_btcoex(struct file *file,2006const char __user *ubuf,2007size_t count, loff_t *ppos)2008{2009struct ath10k *ar = file->private_data;2010ssize_t ret;2011bool val;2012u32 pdev_param;20132014ret = kstrtobool_from_user(ubuf, count, &val);2015if (ret)2016return ret;20172018if (!ar->coex_support)2019return -EOPNOTSUPP;20202021mutex_lock(&ar->conf_mutex);20222023if (ar->state != ATH10K_STATE_ON &&2024ar->state != ATH10K_STATE_RESTARTED) {2025ret = -ENETDOWN;2026goto exit;2027}20282029if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val)) {2030ret = count;2031goto exit;2032}20332034pdev_param = ar->wmi.pdev_param->enable_btcoex;2035if (test_bit(ATH10K_FW_FEATURE_BTCOEX_PARAM,2036ar->running_fw->fw_file.fw_features)) {2037ret = ath10k_wmi_pdev_set_param(ar, pdev_param, val);2038if (ret) {2039ath10k_warn(ar, "failed to enable btcoex: %zd\n", ret);2040ret = count;2041goto exit;2042}2043} else {2044ath10k_info(ar, "restarting firmware due to btcoex change");2045ath10k_core_start_recovery(ar);2046}20472048if (val)2049set_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);2050else2051clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);20522053ret = count;20542055exit:2056mutex_unlock(&ar->conf_mutex);20572058return ret;2059}20602061static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf,2062size_t count, loff_t *ppos)2063{2064char buf[32];2065struct ath10k *ar = file->private_data;2066int len = 0;20672068mutex_lock(&ar->conf_mutex);2069len = scnprintf(buf, sizeof(buf) - len, "%d\n",2070test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags));2071mutex_unlock(&ar->conf_mutex);20722073return simple_read_from_buffer(ubuf, count, ppos, buf, len);2074}20752076static const struct file_operations fops_btcoex = {2077.read = ath10k_read_btcoex,2078.write = ath10k_write_btcoex,2079.open = simple_open2080};20812082static ssize_t ath10k_write_enable_extd_tx_stats(struct file *file,2083const char __user *ubuf,2084size_t count, loff_t *ppos)2085{2086struct ath10k *ar = file->private_data;2087u32 filter;2088int ret;20892090if (kstrtouint_from_user(ubuf, count, 0, &filter))2091return -EINVAL;20922093mutex_lock(&ar->conf_mutex);20942095if (ar->state != ATH10K_STATE_ON) {2096ar->debug.enable_extd_tx_stats = filter;2097ret = count;2098goto out;2099}21002101if (filter == ar->debug.enable_extd_tx_stats) {2102ret = count;2103goto out;2104}21052106ar->debug.enable_extd_tx_stats = filter;2107ret = count;21082109out:2110mutex_unlock(&ar->conf_mutex);2111return ret;2112}21132114static ssize_t ath10k_read_enable_extd_tx_stats(struct file *file,2115char __user *ubuf,2116size_t count, loff_t *ppos)21172118{2119char buf[32];2120struct ath10k *ar = file->private_data;2121int len = 0;21222123mutex_lock(&ar->conf_mutex);2124len = scnprintf(buf, sizeof(buf) - len, "%08x\n",2125ar->debug.enable_extd_tx_stats);2126mutex_unlock(&ar->conf_mutex);21272128return simple_read_from_buffer(ubuf, count, ppos, buf, len);2129}21302131static const struct file_operations fops_enable_extd_tx_stats = {2132.read = ath10k_read_enable_extd_tx_stats,2133.write = ath10k_write_enable_extd_tx_stats,2134.open = simple_open2135};21362137static ssize_t ath10k_write_peer_stats(struct file *file,2138const char __user *ubuf,2139size_t count, loff_t *ppos)2140{2141struct ath10k *ar = file->private_data;2142ssize_t ret;2143bool val;21442145ret = kstrtobool_from_user(ubuf, count, &val);2146if (ret)2147return ret;21482149mutex_lock(&ar->conf_mutex);21502151if (ar->state != ATH10K_STATE_ON &&2152ar->state != ATH10K_STATE_RESTARTED) {2153ret = -ENETDOWN;2154goto exit;2155}21562157if (!(test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags) ^ val)) {2158ret = count;2159goto exit;2160}21612162if (val)2163set_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);2164else2165clear_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);21662167ath10k_info(ar, "restarting firmware due to Peer stats change");21682169ath10k_core_start_recovery(ar);2170ret = count;21712172exit:2173mutex_unlock(&ar->conf_mutex);2174return ret;2175}21762177static ssize_t ath10k_read_peer_stats(struct file *file, char __user *ubuf,2178size_t count, loff_t *ppos)21792180{2181char buf[32];2182struct ath10k *ar = file->private_data;2183int len = 0;21842185mutex_lock(&ar->conf_mutex);2186len = scnprintf(buf, sizeof(buf) - len, "%d\n",2187test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags));2188mutex_unlock(&ar->conf_mutex);21892190return simple_read_from_buffer(ubuf, count, ppos, buf, len);2191}21922193static const struct file_operations fops_peer_stats = {2194.read = ath10k_read_peer_stats,2195.write = ath10k_write_peer_stats,2196.open = simple_open2197};21982199static ssize_t ath10k_debug_fw_checksums_read(struct file *file,2200char __user *user_buf,2201size_t count, loff_t *ppos)2202{2203struct ath10k *ar = file->private_data;2204size_t len = 0, buf_len = 4096;2205ssize_t ret_cnt;2206char *buf;22072208buf = kzalloc(buf_len, GFP_KERNEL);2209if (!buf)2210return -ENOMEM;22112212mutex_lock(&ar->conf_mutex);22132214len += scnprintf(buf + len, buf_len - len,2215"firmware-N.bin\t\t%08x\n",2216crc32_le(0, ar->normal_mode_fw.fw_file.firmware->data,2217ar->normal_mode_fw.fw_file.firmware->size));2218len += scnprintf(buf + len, buf_len - len,2219"athwlan\t\t\t%08x\n",2220crc32_le(0, ar->normal_mode_fw.fw_file.firmware_data,2221ar->normal_mode_fw.fw_file.firmware_len));2222len += scnprintf(buf + len, buf_len - len,2223"otp\t\t\t%08x\n",2224crc32_le(0, ar->normal_mode_fw.fw_file.otp_data,2225ar->normal_mode_fw.fw_file.otp_len));2226len += scnprintf(buf + len, buf_len - len,2227"codeswap\t\t%08x\n",2228crc32_le(0, ar->normal_mode_fw.fw_file.codeswap_data,2229ar->normal_mode_fw.fw_file.codeswap_len));2230len += scnprintf(buf + len, buf_len - len,2231"board-N.bin\t\t%08x\n",2232crc32_le(0, ar->normal_mode_fw.board->data,2233ar->normal_mode_fw.board->size));2234len += scnprintf(buf + len, buf_len - len,2235"board\t\t\t%08x\n",2236crc32_le(0, ar->normal_mode_fw.board_data,2237ar->normal_mode_fw.board_len));22382239ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);22402241mutex_unlock(&ar->conf_mutex);22422243kfree(buf);2244return ret_cnt;2245}22462247static const struct file_operations fops_fw_checksums = {2248.read = ath10k_debug_fw_checksums_read,2249.open = simple_open,2250.owner = THIS_MODULE,2251.llseek = default_llseek,2252};22532254static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file,2255char __user *user_buf,2256size_t count, loff_t *ppos)2257{2258struct ath10k *ar = file->private_data;2259char buf[32];2260size_t len;22612262len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask);2263return simple_read_from_buffer(user_buf, count, ppos, buf, len);2264}22652266static ssize_t ath10k_sta_tid_stats_mask_write(struct file *file,2267const char __user *user_buf,2268size_t count, loff_t *ppos)2269{2270struct ath10k *ar = file->private_data;2271ssize_t ret;2272u32 mask;22732274ret = kstrtoint_from_user(user_buf, count, 0, &mask);2275if (ret)2276return ret;22772278ar->sta_tid_stats_mask = mask;22792280return count;2281}22822283static const struct file_operations fops_sta_tid_stats_mask = {2284.read = ath10k_sta_tid_stats_mask_read,2285.write = ath10k_sta_tid_stats_mask_write,2286.open = simple_open,2287.owner = THIS_MODULE,2288.llseek = default_llseek,2289};22902291static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar)2292{2293int ret;2294unsigned long time_left;22952296lockdep_assert_held(&ar->conf_mutex);22972298reinit_completion(&ar->debug.tpc_complete);22992300ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM);2301if (ret) {2302ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret);2303return ret;2304}23052306time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,23071 * HZ);2308if (time_left == 0)2309return -ETIMEDOUT;23102311return 0;2312}23132314static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file)2315{2316struct ath10k *ar = inode->i_private;2317void *buf;2318int ret;23192320mutex_lock(&ar->conf_mutex);23212322if (ar->state != ATH10K_STATE_ON) {2323ret = -ENETDOWN;2324goto err_unlock;2325}23262327buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);2328if (!buf) {2329ret = -ENOMEM;2330goto err_unlock;2331}23322333ret = ath10k_debug_tpc_stats_final_request(ar);2334if (ret) {2335ath10k_warn(ar, "failed to request tpc stats final: %d\n",2336ret);2337goto err_free;2338}23392340ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);2341file->private_data = buf;23422343mutex_unlock(&ar->conf_mutex);2344return 0;23452346err_free:2347vfree(buf);23482349err_unlock:2350mutex_unlock(&ar->conf_mutex);2351return ret;2352}23532354static int ath10k_tpc_stats_final_release(struct inode *inode,2355struct file *file)2356{2357vfree(file->private_data);23582359return 0;2360}23612362static ssize_t ath10k_tpc_stats_final_read(struct file *file,2363char __user *user_buf,2364size_t count, loff_t *ppos)2365{2366const char *buf = file->private_data;2367unsigned int len = strlen(buf);23682369return simple_read_from_buffer(user_buf, count, ppos, buf, len);2370}23712372static const struct file_operations fops_tpc_stats_final = {2373.open = ath10k_tpc_stats_final_open,2374.release = ath10k_tpc_stats_final_release,2375.read = ath10k_tpc_stats_final_read,2376.owner = THIS_MODULE,2377.llseek = default_llseek,2378};23792380static ssize_t ath10k_write_warm_hw_reset(struct file *file,2381const char __user *user_buf,2382size_t count, loff_t *ppos)2383{2384struct ath10k *ar = file->private_data;2385int ret;2386bool val;23872388if (kstrtobool_from_user(user_buf, count, &val))2389return -EFAULT;23902391if (!val)2392return -EINVAL;23932394mutex_lock(&ar->conf_mutex);23952396if (ar->state != ATH10K_STATE_ON) {2397ret = -ENETDOWN;2398goto exit;2399}24002401ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pdev_reset,2402WMI_RST_MODE_WARM_RESET);24032404if (ret) {2405ath10k_warn(ar, "failed to enable warm hw reset: %d\n", ret);2406goto exit;2407}24082409ret = count;24102411exit:2412mutex_unlock(&ar->conf_mutex);2413return ret;2414}24152416static const struct file_operations fops_warm_hw_reset = {2417.write = ath10k_write_warm_hw_reset,2418.open = simple_open,2419.owner = THIS_MODULE,2420.llseek = default_llseek,2421};24222423static void ath10k_peer_ps_state_disable(void *data,2424struct ieee80211_sta *sta)2425{2426struct ath10k *ar = data;2427struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;24282429spin_lock_bh(&ar->data_lock);2430arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;2431spin_unlock_bh(&ar->data_lock);2432}24332434static ssize_t ath10k_write_ps_state_enable(struct file *file,2435const char __user *user_buf,2436size_t count, loff_t *ppos)2437{2438struct ath10k *ar = file->private_data;2439int ret;2440u32 param;2441u8 ps_state_enable;24422443if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))2444return -EINVAL;24452446if (ps_state_enable > 1)2447return -EINVAL;24482449mutex_lock(&ar->conf_mutex);24502451if (ar->ps_state_enable == ps_state_enable) {2452ret = count;2453goto exit;2454}24552456param = ar->wmi.pdev_param->peer_sta_ps_statechg_enable;2457ret = ath10k_wmi_pdev_set_param(ar, param, ps_state_enable);2458if (ret) {2459ath10k_warn(ar, "failed to enable ps_state_enable: %d\n",2460ret);2461goto exit;2462}2463ar->ps_state_enable = ps_state_enable;24642465if (!ar->ps_state_enable)2466ieee80211_iterate_stations_atomic(ar->hw,2467ath10k_peer_ps_state_disable,2468ar);24692470ret = count;24712472exit:2473mutex_unlock(&ar->conf_mutex);24742475return ret;2476}24772478static ssize_t ath10k_read_ps_state_enable(struct file *file,2479char __user *user_buf,2480size_t count, loff_t *ppos)2481{2482struct ath10k *ar = file->private_data;2483int len = 0;2484char buf[32];24852486mutex_lock(&ar->conf_mutex);2487len = scnprintf(buf, sizeof(buf) - len, "%d\n",2488ar->ps_state_enable);2489mutex_unlock(&ar->conf_mutex);24902491return simple_read_from_buffer(user_buf, count, ppos, buf, len);2492}24932494static const struct file_operations fops_ps_state_enable = {2495.read = ath10k_read_ps_state_enable,2496.write = ath10k_write_ps_state_enable,2497.open = simple_open,2498.owner = THIS_MODULE,2499.llseek = default_llseek,2500};25012502static ssize_t ath10k_write_reset_htt_stats(struct file *file,2503const char __user *user_buf,2504size_t count, loff_t *ppos)2505{2506struct ath10k *ar = file->private_data;2507unsigned long reset;2508int ret;25092510ret = kstrtoul_from_user(user_buf, count, 0, &reset);2511if (ret)2512return ret;25132514if (reset == 0 || reset > 0x1ffff)2515return -EINVAL;25162517mutex_lock(&ar->conf_mutex);25182519ar->debug.reset_htt_stats = reset;25202521ret = ath10k_debug_htt_stats_req(ar);2522if (ret)2523goto out;25242525ar->debug.reset_htt_stats = 0;2526ret = count;25272528out:2529mutex_unlock(&ar->conf_mutex);2530return ret;2531}25322533static const struct file_operations fops_reset_htt_stats = {2534.write = ath10k_write_reset_htt_stats,2535.owner = THIS_MODULE,2536.open = simple_open,2537.llseek = default_llseek,2538};25392540int ath10k_debug_create(struct ath10k *ar)2541{2542ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);2543if (!ar->debug.cal_data)2544return -ENOMEM;25452546INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);2547INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);2548INIT_LIST_HEAD(&ar->debug.fw_stats.peers);2549INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd);25502551return 0;2552}25532554void ath10k_debug_destroy(struct ath10k *ar)2555{2556vfree(ar->debug.cal_data);2557ar->debug.cal_data = NULL;25582559ath10k_debug_fw_stats_reset(ar);25602561kfree(ar->debug.tpc_stats);2562kfree(ar->debug.tpc_stats_final);2563}25642565int ath10k_debug_register(struct ath10k *ar)2566{2567ar->debug.debugfs_phy = debugfs_create_dir("ath10k",2568ar->hw->wiphy->debugfsdir);2569if (IS_ERR_OR_NULL(ar->debug.debugfs_phy)) {2570if (IS_ERR(ar->debug.debugfs_phy))2571return PTR_ERR(ar->debug.debugfs_phy);25722573return -ENOMEM;2574}25752576INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,2577ath10k_debug_htt_stats_dwork);25782579init_completion(&ar->debug.tpc_complete);2580init_completion(&ar->debug.fw_stats_complete);25812582debugfs_create_file("fw_stats", 0400, ar->debug.debugfs_phy, ar,2583&fops_fw_stats);25842585debugfs_create_file("fw_reset_stats", 0400, ar->debug.debugfs_phy, ar,2586&fops_fw_reset_stats);25872588debugfs_create_file("wmi_services", 0400, ar->debug.debugfs_phy, ar,2589&fops_wmi_services);25902591debugfs_create_file("simulate_fw_crash", 0600, ar->debug.debugfs_phy, ar,2592&fops_simulate_fw_crash);25932594debugfs_create_file("reg_addr", 0600, ar->debug.debugfs_phy, ar,2595&fops_reg_addr);25962597debugfs_create_file("reg_value", 0600, ar->debug.debugfs_phy, ar,2598&fops_reg_value);25992600debugfs_create_file("mem_value", 0600, ar->debug.debugfs_phy, ar,2601&fops_mem_value);26022603debugfs_create_file("chip_id", 0400, ar->debug.debugfs_phy, ar,2604&fops_chip_id);26052606debugfs_create_file("htt_stats_mask", 0600, ar->debug.debugfs_phy, ar,2607&fops_htt_stats_mask);26082609debugfs_create_file("htt_max_amsdu_ampdu", 0600, ar->debug.debugfs_phy, ar,2610&fops_htt_max_amsdu_ampdu);26112612debugfs_create_file("fw_dbglog", 0600, ar->debug.debugfs_phy, ar,2613&fops_fw_dbglog);26142615if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,2616ar->normal_mode_fw.fw_file.fw_features)) {2617debugfs_create_file("cal_data", 0400, ar->debug.debugfs_phy, ar,2618&fops_cal_data);26192620debugfs_create_file("nf_cal_period", 0600, ar->debug.debugfs_phy, ar,2621&fops_nf_cal_period);2622}26232624debugfs_create_file("ani_enable", 0600, ar->debug.debugfs_phy, ar,2625&fops_ani_enable);26262627if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {2628debugfs_create_file("dfs_simulate_radar", 0200, ar->debug.debugfs_phy,2629ar, &fops_simulate_radar);26302631debugfs_create_bool("dfs_block_radar_events", 0200,2632ar->debug.debugfs_phy,2633&ar->dfs_block_radar_events);26342635debugfs_create_file("dfs_stats", 0400, ar->debug.debugfs_phy, ar,2636&fops_dfs_stats);2637}26382639debugfs_create_file("pktlog_filter", 0644, ar->debug.debugfs_phy, ar,2640&fops_pktlog_filter);26412642if (test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))2643debugfs_create_file("quiet_period", 0644, ar->debug.debugfs_phy, ar,2644&fops_quiet_period);26452646debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_phy, ar,2647&fops_tpc_stats);26482649if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))2650debugfs_create_file("btcoex", 0644, ar->debug.debugfs_phy, ar,2651&fops_btcoex);26522653if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {2654debugfs_create_file("peer_stats", 0644, ar->debug.debugfs_phy, ar,2655&fops_peer_stats);26562657debugfs_create_file("enable_extd_tx_stats", 0644,2658ar->debug.debugfs_phy, ar,2659&fops_enable_extd_tx_stats);2660}26612662debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar,2663&fops_fw_checksums);26642665if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS))2666debugfs_create_file("sta_tid_stats_mask", 0600,2667ar->debug.debugfs_phy,2668ar, &fops_sta_tid_stats_mask);26692670if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map))2671debugfs_create_file("tpc_stats_final", 0400,2672ar->debug.debugfs_phy, ar,2673&fops_tpc_stats_final);26742675if (test_bit(WMI_SERVICE_RESET_CHIP, ar->wmi.svc_map))2676debugfs_create_file("warm_hw_reset", 0600,2677ar->debug.debugfs_phy, ar,2678&fops_warm_hw_reset);26792680debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_phy, ar,2681&fops_ps_state_enable);26822683debugfs_create_file("reset_htt_stats", 0200, ar->debug.debugfs_phy, ar,2684&fops_reset_htt_stats);26852686return 0;2687}26882689void ath10k_debug_unregister(struct ath10k *ar)2690{2691cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);2692}26932694#endif /* CONFIG_ATH10K_DEBUGFS */26952696#ifdef CONFIG_ATH10K_DEBUG2697void __ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask,2698const char *fmt, ...)2699{2700struct va_format vaf;2701va_list args;27022703va_start(args, fmt);27042705vaf.fmt = fmt;2706vaf.va = &args;27072708if (ath10k_debug_mask & mask)2709#if defined(__linux__)2710dev_printk(KERN_DEBUG, ar->dev, "%pV", &vaf);2711#elif defined(__FreeBSD__)2712{2713char *str;2714vasprintf(&str, M_KMALLOC, fmt, args);2715dev_printk(KERN_DEBUG, ar->dev, "%s", str);2716free(str, M_KMALLOC);2717}2718#endif27192720trace_ath10k_log_dbg(ar, mask, &vaf);27212722va_end(args);2723}2724EXPORT_SYMBOL(__ath10k_dbg);27252726void ath10k_dbg_dump(struct ath10k *ar,2727enum ath10k_debug_mask mask,2728const char *msg, const char *prefix,2729const void *buf, size_t len)2730{2731#if defined(__linux__)2732char linebuf[256];2733size_t linebuflen;2734const void *ptr;2735#elif defined(__FreeBSD__)2736struct sbuf *sb;2737int rc;2738#endif27392740if (ath10k_debug_mask & mask) {2741if (msg)2742__ath10k_dbg(ar, mask, "%s\n", msg);27432744#if defined(__linux__)2745for (ptr = buf; (ptr - buf) < len; ptr += 16) {2746linebuflen = 0;2747linebuflen += scnprintf(linebuf + linebuflen,2748sizeof(linebuf) - linebuflen,2749"%s%08x: ",2750(prefix ? prefix : ""),2751(unsigned int)(ptr - buf));2752hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,2753linebuf + linebuflen,2754sizeof(linebuf) - linebuflen, true);2755dev_printk(KERN_DEBUG, ar->dev, "%s\n", linebuf);2756}2757#elif defined(__FreeBSD__)2758sb = sbuf_new_auto();2759if (sb == NULL)2760goto trace;27612762sbuf_hexdump(sb, buf, len, prefix, 0);2763sbuf_trim(sb);2764rc = sbuf_finish(sb);2765if (rc == 0)2766dev_printk(KERN_DEBUG, ar->dev, "%s\n", sbuf_data(sb));2767sbuf_delete(sb);2768trace: ;2769#endif2770}27712772/* tracing code doesn't like null strings :/ */2773trace_ath10k_log_dbg_dump(ar, msg ? msg : "", prefix ? prefix : "",2774buf, len);2775}2776EXPORT_SYMBOL(ath10k_dbg_dump);27772778#endif /* CONFIG_ATH10K_DEBUG */27792780#if defined(__FreeBSD__)2781#ifdef CONFIG_ATH10K_DEBUGFS2782MODULE_DEPEND(ath10k, debugfs, 1, 1, 1);2783#endif2784#endif278527862787