Path: blob/main/sys/contrib/dev/athk/ath10k/debug.c
48375 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*/67#include <linux/module.h>8#include <linux/debugfs.h>9#include <linux/vmalloc.h>10#include <linux/crc32.h>11#include <linux/firmware.h>12#include <linux/kstrtox.h>1314#if defined(__FreeBSD__)15#ifdef CONFIG_ATH10K_DEBUG16#include <sys/sbuf.h>17#endif18#endif1920#include "core.h"21#include "debug.h"22#include "hif.h"23#include "wmi-ops.h"2425/* ms */26#define ATH10K_DEBUG_HTT_STATS_INTERVAL 10002728#define ATH10K_DEBUG_CAL_DATA_LEN 120642930void ath10k_info(struct ath10k *ar, const char *fmt, ...)31{32struct va_format vaf = {33.fmt = fmt,34};35va_list args;3637va_start(args, fmt);38vaf.va = &args;39#if defined(__linux__)40dev_info(ar->dev, "%pV", &vaf);41#elif defined(__FreeBSD__)42{43char *str;44vasprintf(&str, M_KMALLOC, fmt, args);45dev_printk(KERN_DEBUG, ar->dev, "%s", str);46free(str, M_KMALLOC);47}48#endif49trace_ath10k_log_info(ar, &vaf);50va_end(args);51}52EXPORT_SYMBOL(ath10k_info);5354void ath10k_debug_print_hwfw_info(struct ath10k *ar)55{56const struct firmware *firmware;57char fw_features[128] = {};58u32 crc = 0;5960ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));6162#if defined(__linux__)63ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x",64#elif defined(__FreeBSD__)65ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x\n",66#endif67ar->hw_params.name,68ar->target_version,69ar->bus_param.chip_id,70ar->id.subsystem_vendor, ar->id.subsystem_device);7172ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n",73IS_ENABLED(CONFIG_ATH10K_DEBUG),74IS_ENABLED(CONFIG_ATH10K_DEBUGFS),75IS_ENABLED(CONFIG_ATH10K_TRACING),76IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED),77IS_ENABLED(CONFIG_NL80211_TESTMODE));7879firmware = ar->normal_mode_fw.fw_file.firmware;80if (firmware)81crc = crc32_le(0, firmware->data, firmware->size);8283ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n",84ar->hw->wiphy->fw_version,85ar->fw_api,86fw_features,87crc);88}8990void ath10k_debug_print_board_info(struct ath10k *ar)91{92char boardinfo[100];93const struct firmware *board;94u32 crc;9596if (ar->id.bmi_ids_valid)97scnprintf(boardinfo, sizeof(boardinfo), "%d:%d",98ar->id.bmi_chip_id, ar->id.bmi_board_id);99else100scnprintf(boardinfo, sizeof(boardinfo), "N/A");101102board = ar->normal_mode_fw.board;103if (!IS_ERR_OR_NULL(board))104crc = crc32_le(0, board->data, board->size);105else106crc = 0;107108#if defined(__linux__)109ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x",110#elif defined(__FreeBSD__)111ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x\n",112#endif113ar->bd_api,114boardinfo,115crc);116}117118void ath10k_debug_print_boot_info(struct ath10k *ar)119{120ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n",121ar->htt.target_version_major,122ar->htt.target_version_minor,123ar->normal_mode_fw.fw_file.wmi_op_version,124ar->normal_mode_fw.fw_file.htt_op_version,125ath10k_cal_mode_str(ar->cal_mode),126ar->max_num_stations,127test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags),128!test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags));129}130131void ath10k_print_driver_info(struct ath10k *ar)132{133ath10k_debug_print_hwfw_info(ar);134ath10k_debug_print_board_info(ar);135ath10k_debug_print_boot_info(ar);136}137EXPORT_SYMBOL(ath10k_print_driver_info);138139void ath10k_err(struct ath10k *ar, const char *fmt, ...)140{141struct va_format vaf = {142.fmt = fmt,143};144va_list args;145146va_start(args, fmt);147vaf.va = &args;148#if defined(__linux__)149dev_err(ar->dev, "%pV", &vaf);150#elif defined(__FreeBSD__)151{152char *str;153vasprintf(&str, M_KMALLOC, fmt, args);154dev_err(ar->dev, "%s", str);155free(str, M_KMALLOC);156}157#endif158trace_ath10k_log_err(ar, &vaf);159va_end(args);160}161EXPORT_SYMBOL(ath10k_err);162163void ath10k_warn(struct ath10k *ar, const char *fmt, ...)164{165struct va_format vaf = {166.fmt = fmt,167};168va_list args;169170va_start(args, fmt);171vaf.va = &args;172#if defined(__linux__)173dev_warn_ratelimited(ar->dev, "%pV", &vaf);174#elif defined(__FreeBSD__)175{176char *str;177vasprintf(&str, M_KMALLOC, fmt, args);178dev_warn_ratelimited(ar->dev, "%s", str);179free(str, M_KMALLOC);180}181#endif182trace_ath10k_log_warn(ar, &vaf);183184va_end(args);185}186EXPORT_SYMBOL(ath10k_warn);187188#ifdef CONFIG_ATH10K_DEBUGFS189190static ssize_t ath10k_read_wmi_services(struct file *file,191char __user *user_buf,192size_t count, loff_t *ppos)193{194struct ath10k *ar = file->private_data;195char *buf;196size_t len = 0, buf_len = 8192;197const char *name;198ssize_t ret_cnt;199bool enabled;200int i;201202buf = kzalloc(buf_len, GFP_KERNEL);203if (!buf)204return -ENOMEM;205206mutex_lock(&ar->conf_mutex);207208spin_lock_bh(&ar->data_lock);209for (i = 0; i < WMI_SERVICE_MAX; i++) {210enabled = test_bit(i, ar->wmi.svc_map);211name = wmi_service_name(i);212213if (!name) {214if (enabled)215len += scnprintf(buf + len, buf_len - len,216"%-40s %s (bit %d)\n",217"unknown", "enabled", i);218219continue;220}221222len += scnprintf(buf + len, buf_len - len,223"%-40s %s\n",224name, enabled ? "enabled" : "-");225}226spin_unlock_bh(&ar->data_lock);227228ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);229230mutex_unlock(&ar->conf_mutex);231232kfree(buf);233return ret_cnt;234}235236static const struct file_operations fops_wmi_services = {237.read = ath10k_read_wmi_services,238.open = simple_open,239.owner = THIS_MODULE,240.llseek = default_llseek,241};242243static void ath10k_fw_stats_pdevs_free(struct list_head *head)244{245struct ath10k_fw_stats_pdev *i, *tmp;246247list_for_each_entry_safe(i, tmp, head, list) {248list_del(&i->list);249kfree(i);250}251}252253static void ath10k_fw_stats_vdevs_free(struct list_head *head)254{255struct ath10k_fw_stats_vdev *i, *tmp;256257list_for_each_entry_safe(i, tmp, head, list) {258list_del(&i->list);259kfree(i);260}261}262263static void ath10k_fw_stats_peers_free(struct list_head *head)264{265struct ath10k_fw_stats_peer *i, *tmp;266267list_for_each_entry_safe(i, tmp, head, list) {268list_del(&i->list);269kfree(i);270}271}272273static void ath10k_fw_extd_stats_peers_free(struct list_head *head)274{275struct ath10k_fw_extd_stats_peer *i, *tmp;276277list_for_each_entry_safe(i, tmp, head, list) {278list_del(&i->list);279kfree(i);280}281}282283static void ath10k_debug_fw_stats_reset(struct ath10k *ar)284{285spin_lock_bh(&ar->data_lock);286ar->debug.fw_stats_done = false;287ar->debug.fw_stats.extended = false;288ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);289ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);290ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);291ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);292spin_unlock_bh(&ar->data_lock);293}294295void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)296{297struct ath10k_fw_stats stats = {};298bool is_start, is_started, is_end;299size_t num_peers;300size_t num_vdevs;301int ret;302303INIT_LIST_HEAD(&stats.pdevs);304INIT_LIST_HEAD(&stats.vdevs);305INIT_LIST_HEAD(&stats.peers);306INIT_LIST_HEAD(&stats.peers_extd);307308spin_lock_bh(&ar->data_lock);309ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);310if (ret) {311ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);312goto free;313}314315/* Stat data may exceed htc-wmi buffer limit. In such case firmware316* splits the stats data and delivers it in a ping-pong fashion of317* request cmd-update event.318*319* However there is no explicit end-of-data. Instead start-of-data is320* used as an implicit one. This works as follows:321* a) discard stat update events until one with pdev stats is322* delivered - this skips session started at end of (b)323* b) consume stat update events until another one with pdev stats is324* delivered which is treated as end-of-data and is itself discarded325*/326if (ath10k_peer_stats_enabled(ar))327ath10k_sta_update_rx_duration(ar, &stats);328329if (ar->debug.fw_stats_done) {330if (!ath10k_peer_stats_enabled(ar))331ath10k_warn(ar, "received unsolicited stats update event\n");332333goto free;334}335336num_peers = list_count_nodes(&ar->debug.fw_stats.peers);337num_vdevs = list_count_nodes(&ar->debug.fw_stats.vdevs);338is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&339!list_empty(&stats.pdevs));340is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&341!list_empty(&stats.pdevs));342343if (is_start)344list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);345346if (is_end)347ar->debug.fw_stats_done = true;348349if (stats.extended)350ar->debug.fw_stats.extended = true;351352is_started = !list_empty(&ar->debug.fw_stats.pdevs);353354if (is_started && !is_end) {355if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {356/* Although this is unlikely impose a sane limit to357* prevent firmware from DoS-ing the host.358*/359ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers);360ath10k_fw_extd_stats_peers_free(&ar->debug.fw_stats.peers_extd);361ath10k_warn(ar, "dropping fw peer stats\n");362goto free;363}364365if (num_vdevs >= BITS_PER_LONG) {366ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs);367ath10k_warn(ar, "dropping fw vdev stats\n");368goto free;369}370371if (!list_empty(&stats.peers))372list_splice_tail_init(&stats.peers_extd,373&ar->debug.fw_stats.peers_extd);374375list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);376list_splice_tail_init(&stats.vdevs, &ar->debug.fw_stats.vdevs);377}378379complete(&ar->debug.fw_stats_complete);380381free:382/* In some cases lists have been spliced and cleared. Free up383* resources if that is not the case.384*/385ath10k_fw_stats_pdevs_free(&stats.pdevs);386ath10k_fw_stats_vdevs_free(&stats.vdevs);387ath10k_fw_stats_peers_free(&stats.peers);388ath10k_fw_extd_stats_peers_free(&stats.peers_extd);389390spin_unlock_bh(&ar->data_lock);391}392393int ath10k_debug_fw_stats_request(struct ath10k *ar)394{395unsigned long timeout, time_left;396int ret;397398lockdep_assert_held(&ar->conf_mutex);399400timeout = jiffies + msecs_to_jiffies(1 * HZ);401402ath10k_debug_fw_stats_reset(ar);403404for (;;) {405if (time_after(jiffies, timeout))406return -ETIMEDOUT;407408reinit_completion(&ar->debug.fw_stats_complete);409410ret = ath10k_wmi_request_stats(ar, ar->fw_stats_req_mask);411if (ret) {412ath10k_warn(ar, "could not request stats (%d)\n", ret);413return ret;414}415416time_left =417wait_for_completion_timeout(&ar->debug.fw_stats_complete,4181 * HZ);419if (!time_left)420return -ETIMEDOUT;421422spin_lock_bh(&ar->data_lock);423if (ar->debug.fw_stats_done) {424spin_unlock_bh(&ar->data_lock);425break;426}427spin_unlock_bh(&ar->data_lock);428}429430return 0;431}432433static int ath10k_fw_stats_open(struct inode *inode, struct file *file)434{435struct ath10k *ar = inode->i_private;436void *buf = NULL;437int ret;438439mutex_lock(&ar->conf_mutex);440441if (ar->state != ATH10K_STATE_ON) {442ret = -ENETDOWN;443goto err_unlock;444}445446buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);447if (!buf) {448ret = -ENOMEM;449goto err_unlock;450}451452ret = ath10k_debug_fw_stats_request(ar);453if (ret) {454ath10k_warn(ar, "failed to request fw stats: %d\n", ret);455goto err_free;456}457458ret = ath10k_wmi_fw_stats_fill(ar, &ar->debug.fw_stats, buf);459if (ret) {460ath10k_warn(ar, "failed to fill fw stats: %d\n", ret);461goto err_free;462}463464file->private_data = buf;465466mutex_unlock(&ar->conf_mutex);467return 0;468469err_free:470vfree(buf);471472err_unlock:473mutex_unlock(&ar->conf_mutex);474return ret;475}476477static int ath10k_fw_stats_release(struct inode *inode, struct file *file)478{479vfree(file->private_data);480481return 0;482}483484static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,485size_t count, loff_t *ppos)486{487const char *buf = file->private_data;488size_t len = strlen(buf);489490return simple_read_from_buffer(user_buf, count, ppos, buf, len);491}492493static const struct file_operations fops_fw_stats = {494.open = ath10k_fw_stats_open,495.release = ath10k_fw_stats_release,496.read = ath10k_fw_stats_read,497.owner = THIS_MODULE,498.llseek = default_llseek,499};500501static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,502char __user *user_buf,503size_t count, loff_t *ppos)504{505struct ath10k *ar = file->private_data;506int ret;507size_t len = 0, buf_len = 500;508char *buf;509510buf = kmalloc(buf_len, GFP_KERNEL);511if (!buf)512return -ENOMEM;513514spin_lock_bh(&ar->data_lock);515516len += scnprintf(buf + len, buf_len - len,517"fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);518len += scnprintf(buf + len, buf_len - len,519"fw_warm_reset_counter\t\t%d\n",520ar->stats.fw_warm_reset_counter);521len += scnprintf(buf + len, buf_len - len,522"fw_cold_reset_counter\t\t%d\n",523ar->stats.fw_cold_reset_counter);524525spin_unlock_bh(&ar->data_lock);526527ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);528529kfree(buf);530531return ret;532}533534static const struct file_operations fops_fw_reset_stats = {535.open = simple_open,536.read = ath10k_debug_fw_reset_stats_read,537.owner = THIS_MODULE,538.llseek = default_llseek,539};540541/* This is a clean assert crash in firmware. */542static int ath10k_debug_fw_assert(struct ath10k *ar)543{544struct wmi_vdev_install_key_cmd *cmd;545struct sk_buff *skb;546547skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + 16);548if (!skb)549return -ENOMEM;550551cmd = (struct wmi_vdev_install_key_cmd *)skb->data;552memset(cmd, 0, sizeof(*cmd));553554/* big enough number so that firmware asserts */555cmd->vdev_id = __cpu_to_le32(0x7ffe);556557return ath10k_wmi_cmd_send(ar, skb,558ar->wmi.cmd->vdev_install_key_cmdid);559}560561static ssize_t ath10k_read_simulate_fw_crash(struct file *file,562char __user *user_buf,563size_t count, loff_t *ppos)564{565const char buf[] =566"To simulate firmware crash write one of the keywords to this file:\n"567"`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"568"`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"569"`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"570"`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";571572return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));573}574575/* Simulate firmware crash:576* 'soft': Call wmi command causing firmware hang. This firmware hang is577* recoverable by warm firmware reset.578* 'hard': Force firmware crash by setting any vdev parameter for not allowed579* vdev id. This is hard firmware crash because it is recoverable only by cold580* firmware reset.581*/582static ssize_t ath10k_write_simulate_fw_crash(struct file *file,583const char __user *user_buf,584size_t count, loff_t *ppos)585{586struct ath10k *ar = file->private_data;587char buf[32] = {0};588ssize_t rc;589int ret;590591/* filter partial writes and invalid commands */592if (*ppos != 0 || count >= sizeof(buf) || count == 0)593return -EINVAL;594595rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);596if (rc < 0)597return rc;598599/* drop the possible '\n' from the end */600if (buf[*ppos - 1] == '\n')601buf[*ppos - 1] = '\0';602603mutex_lock(&ar->conf_mutex);604605if (ar->state != ATH10K_STATE_ON &&606ar->state != ATH10K_STATE_RESTARTED) {607ret = -ENETDOWN;608goto exit;609}610611if (!strcmp(buf, "soft")) {612ath10k_info(ar, "simulating soft firmware crash\n");613ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0);614} else if (!strcmp(buf, "hard")) {615ath10k_info(ar, "simulating hard firmware crash\n");616/* 0x7fff is vdev id, and it is always out of range for all617* firmware variants in order to force a firmware crash.618*/619ret = ath10k_wmi_vdev_set_param(ar, 0x7fff,620ar->wmi.vdev_param->rts_threshold,6210);622} else if (!strcmp(buf, "assert")) {623ath10k_info(ar, "simulating firmware assert crash\n");624ret = ath10k_debug_fw_assert(ar);625} else if (!strcmp(buf, "hw-restart")) {626ath10k_info(ar, "user requested hw restart\n");627ath10k_core_start_recovery(ar);628ret = 0;629} else {630ret = -EINVAL;631goto exit;632}633634if (ret) {635ath10k_warn(ar, "failed to simulate firmware crash: %d\n", ret);636goto exit;637}638639ret = count;640641exit:642mutex_unlock(&ar->conf_mutex);643return ret;644}645646static const struct file_operations fops_simulate_fw_crash = {647.read = ath10k_read_simulate_fw_crash,648.write = ath10k_write_simulate_fw_crash,649.open = simple_open,650.owner = THIS_MODULE,651.llseek = default_llseek,652};653654static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf,655size_t count, loff_t *ppos)656{657struct ath10k *ar = file->private_data;658size_t len;659char buf[50];660661len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->bus_param.chip_id);662663return simple_read_from_buffer(user_buf, count, ppos, buf, len);664}665666static const struct file_operations fops_chip_id = {667.read = ath10k_read_chip_id,668.open = simple_open,669.owner = THIS_MODULE,670.llseek = default_llseek,671};672673static ssize_t ath10k_reg_addr_read(struct file *file,674char __user *user_buf,675size_t count, loff_t *ppos)676{677struct ath10k *ar = file->private_data;678u8 buf[32];679size_t len = 0;680u32 reg_addr;681682mutex_lock(&ar->conf_mutex);683reg_addr = ar->debug.reg_addr;684mutex_unlock(&ar->conf_mutex);685686len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);687688return simple_read_from_buffer(user_buf, count, ppos, buf, len);689}690691static ssize_t ath10k_reg_addr_write(struct file *file,692const char __user *user_buf,693size_t count, loff_t *ppos)694{695struct ath10k *ar = file->private_data;696u32 reg_addr;697int ret;698699ret = kstrtou32_from_user(user_buf, count, 0, ®_addr);700if (ret)701return ret;702703if (!IS_ALIGNED(reg_addr, 4))704return -EFAULT;705706mutex_lock(&ar->conf_mutex);707ar->debug.reg_addr = reg_addr;708mutex_unlock(&ar->conf_mutex);709710return count;711}712713static const struct file_operations fops_reg_addr = {714.read = ath10k_reg_addr_read,715.write = ath10k_reg_addr_write,716.open = simple_open,717.owner = THIS_MODULE,718.llseek = default_llseek,719};720721static ssize_t ath10k_reg_value_read(struct file *file,722char __user *user_buf,723size_t count, loff_t *ppos)724{725struct ath10k *ar = file->private_data;726u8 buf[48];727size_t len;728u32 reg_addr, reg_val;729int ret;730731mutex_lock(&ar->conf_mutex);732733if (ar->state != ATH10K_STATE_ON &&734ar->state != ATH10K_STATE_UTF) {735ret = -ENETDOWN;736goto exit;737}738739reg_addr = ar->debug.reg_addr;740741reg_val = ath10k_hif_read32(ar, reg_addr);742len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);743744ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);745746exit:747mutex_unlock(&ar->conf_mutex);748749return ret;750}751752static ssize_t ath10k_reg_value_write(struct file *file,753const char __user *user_buf,754size_t count, loff_t *ppos)755{756struct ath10k *ar = file->private_data;757u32 reg_addr, reg_val;758int ret;759760mutex_lock(&ar->conf_mutex);761762if (ar->state != ATH10K_STATE_ON &&763ar->state != ATH10K_STATE_UTF) {764ret = -ENETDOWN;765goto exit;766}767768reg_addr = ar->debug.reg_addr;769770ret = kstrtou32_from_user(user_buf, count, 0, ®_val);771if (ret)772goto exit;773774ath10k_hif_write32(ar, reg_addr, reg_val);775776ret = count;777778exit:779mutex_unlock(&ar->conf_mutex);780781return ret;782}783784static const struct file_operations fops_reg_value = {785.read = ath10k_reg_value_read,786.write = ath10k_reg_value_write,787.open = simple_open,788.owner = THIS_MODULE,789.llseek = default_llseek,790};791792static ssize_t ath10k_mem_value_read(struct file *file,793char __user *user_buf,794size_t count, loff_t *ppos)795{796struct ath10k *ar = file->private_data;797u8 *buf;798int ret;799800if (*ppos < 0)801return -EINVAL;802803if (!count)804return 0;805806mutex_lock(&ar->conf_mutex);807808buf = vmalloc(count);809if (!buf) {810ret = -ENOMEM;811goto exit;812}813814if (ar->state != ATH10K_STATE_ON &&815ar->state != ATH10K_STATE_UTF) {816ret = -ENETDOWN;817goto exit;818}819820ret = ath10k_hif_diag_read(ar, *ppos, buf, count);821if (ret) {822ath10k_warn(ar, "failed to read address 0x%08x via diagnose window from debugfs: %d\n",823(u32)(*ppos), ret);824goto exit;825}826827ret = copy_to_user(user_buf, buf, count);828if (ret) {829ret = -EFAULT;830goto exit;831}832833count -= ret;834*ppos += count;835ret = count;836837exit:838vfree(buf);839mutex_unlock(&ar->conf_mutex);840841return ret;842}843844static ssize_t ath10k_mem_value_write(struct file *file,845const char __user *user_buf,846size_t count, loff_t *ppos)847{848struct ath10k *ar = file->private_data;849u8 *buf;850int ret;851852if (*ppos < 0)853return -EINVAL;854855if (!count)856return 0;857858mutex_lock(&ar->conf_mutex);859860buf = vmalloc(count);861if (!buf) {862ret = -ENOMEM;863goto exit;864}865866if (ar->state != ATH10K_STATE_ON &&867ar->state != ATH10K_STATE_UTF) {868ret = -ENETDOWN;869goto exit;870}871872ret = copy_from_user(buf, user_buf, count);873if (ret) {874ret = -EFAULT;875goto exit;876}877878ret = ath10k_hif_diag_write(ar, *ppos, buf, count);879if (ret) {880ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",881(u32)(*ppos), ret);882goto exit;883}884885*ppos += count;886ret = count;887888exit:889vfree(buf);890mutex_unlock(&ar->conf_mutex);891892return ret;893}894895static const struct file_operations fops_mem_value = {896.read = ath10k_mem_value_read,897.write = ath10k_mem_value_write,898.open = simple_open,899.owner = THIS_MODULE,900.llseek = default_llseek,901};902903static int ath10k_debug_htt_stats_req(struct ath10k *ar)904{905u64 cookie;906int ret;907908lockdep_assert_held(&ar->conf_mutex);909910if (ar->debug.htt_stats_mask == 0)911/* htt stats are disabled */912return 0;913914if (ar->state != ATH10K_STATE_ON)915return 0;916917cookie = get_jiffies_64();918919ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask,920ar->debug.reset_htt_stats, cookie);921if (ret) {922ath10k_warn(ar, "failed to send htt stats request: %d\n", ret);923return ret;924}925926queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork,927msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL));928929return 0;930}931932static void ath10k_debug_htt_stats_dwork(struct work_struct *work)933{934struct ath10k *ar = container_of(work, struct ath10k,935debug.htt_stats_dwork.work);936937mutex_lock(&ar->conf_mutex);938939ath10k_debug_htt_stats_req(ar);940941mutex_unlock(&ar->conf_mutex);942}943944static ssize_t ath10k_read_htt_stats_mask(struct file *file,945char __user *user_buf,946size_t count, loff_t *ppos)947{948struct ath10k *ar = file->private_data;949char buf[32];950size_t len;951952len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask);953954return simple_read_from_buffer(user_buf, count, ppos, buf, len);955}956957static ssize_t ath10k_write_htt_stats_mask(struct file *file,958const char __user *user_buf,959size_t count, loff_t *ppos)960{961struct ath10k *ar = file->private_data;962unsigned long mask;963int ret;964965ret = kstrtoul_from_user(user_buf, count, 0, &mask);966if (ret)967return ret;968969/* max 17 bit masks (for now) */970if (mask > HTT_STATS_BIT_MASK)971return -E2BIG;972973mutex_lock(&ar->conf_mutex);974975ar->debug.htt_stats_mask = mask;976977ret = ath10k_debug_htt_stats_req(ar);978if (ret)979goto out;980981ret = count;982983out:984mutex_unlock(&ar->conf_mutex);985986return ret;987}988989static const struct file_operations fops_htt_stats_mask = {990.read = ath10k_read_htt_stats_mask,991.write = ath10k_write_htt_stats_mask,992.open = simple_open,993.owner = THIS_MODULE,994.llseek = default_llseek,995};996997static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,998char __user *user_buf,999size_t count, loff_t *ppos)1000{1001struct ath10k *ar = file->private_data;1002char buf[64];1003u8 amsdu, ampdu;1004size_t len;10051006mutex_lock(&ar->conf_mutex);10071008amsdu = ar->htt.max_num_amsdu;1009ampdu = ar->htt.max_num_ampdu;1010mutex_unlock(&ar->conf_mutex);10111012len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);10131014return simple_read_from_buffer(user_buf, count, ppos, buf, len);1015}10161017static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,1018const char __user *user_buf,1019size_t count, loff_t *ppos)1020{1021struct ath10k *ar = file->private_data;1022int res;1023char buf[64] = {0};1024unsigned int amsdu, ampdu;10251026res = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,1027user_buf, count);1028if (res <= 0)1029return res;10301031res = sscanf(buf, "%u %u", &amsdu, &du);10321033if (res != 2)1034return -EINVAL;10351036mutex_lock(&ar->conf_mutex);10371038res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);1039if (res)1040goto out;10411042res = count;1043ar->htt.max_num_amsdu = amsdu;1044ar->htt.max_num_ampdu = ampdu;10451046out:1047mutex_unlock(&ar->conf_mutex);1048return res;1049}10501051static const struct file_operations fops_htt_max_amsdu_ampdu = {1052.read = ath10k_read_htt_max_amsdu_ampdu,1053.write = ath10k_write_htt_max_amsdu_ampdu,1054.open = simple_open,1055.owner = THIS_MODULE,1056.llseek = default_llseek,1057};10581059static ssize_t ath10k_read_fw_dbglog(struct file *file,1060char __user *user_buf,1061size_t count, loff_t *ppos)1062{1063struct ath10k *ar = file->private_data;1064size_t len;1065char buf[96];10661067len = scnprintf(buf, sizeof(buf), "0x%16llx %u\n",1068ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level);10691070return simple_read_from_buffer(user_buf, count, ppos, buf, len);1071}10721073static ssize_t ath10k_write_fw_dbglog(struct file *file,1074const char __user *user_buf,1075size_t count, loff_t *ppos)1076{1077struct ath10k *ar = file->private_data;1078int ret;1079char buf[96] = {0};1080unsigned int log_level;1081u64 mask;10821083ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,1084user_buf, count);1085if (ret <= 0)1086return ret;10871088ret = sscanf(buf, "%llx %u", &mask, &log_level);10891090if (!ret)1091return -EINVAL;10921093if (ret == 1)1094/* default if user did not specify */1095log_level = ATH10K_DBGLOG_LEVEL_WARN;10961097mutex_lock(&ar->conf_mutex);10981099ar->debug.fw_dbglog_mask = mask;1100ar->debug.fw_dbglog_level = log_level;11011102if (ar->state == ATH10K_STATE_ON) {1103ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,1104ar->debug.fw_dbglog_level);1105if (ret) {1106ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n",1107ret);1108goto exit;1109}1110}11111112ret = count;11131114exit:1115mutex_unlock(&ar->conf_mutex);11161117return ret;1118}11191120/* TODO: Would be nice to always support ethtool stats, would need to1121* move the stats storage out of ath10k_debug, or always have ath10k_debug1122* struct available..1123*/11241125/* This generally corresponds to the debugfs fw_stats file */1126static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = {1127"tx_pkts_nic",1128"tx_bytes_nic",1129"rx_pkts_nic",1130"rx_bytes_nic",1131"d_noise_floor",1132"d_cycle_count",1133"d_phy_error",1134"d_rts_bad",1135"d_rts_good",1136"d_tx_power", /* in .5 dbM I think */1137"d_rx_crc_err", /* fcs_bad */1138"d_rx_crc_err_drop", /* frame with FCS error, dropped late in kernel */1139"d_no_beacon",1140"d_tx_mpdus_queued",1141"d_tx_msdu_queued",1142"d_tx_msdu_dropped",1143"d_local_enqued",1144"d_local_freed",1145"d_tx_ppdu_hw_queued",1146"d_tx_ppdu_reaped",1147"d_tx_fifo_underrun",1148"d_tx_ppdu_abort",1149"d_tx_mpdu_requeued",1150"d_tx_excessive_retries",1151"d_tx_hw_rate",1152"d_tx_dropped_sw_retries",1153"d_tx_illegal_rate",1154"d_tx_continuous_xretries",1155"d_tx_timeout",1156"d_tx_mpdu_txop_limit",1157"d_pdev_resets",1158"d_rx_mid_ppdu_route_change",1159"d_rx_status",1160"d_rx_extra_frags_ring0",1161"d_rx_extra_frags_ring1",1162"d_rx_extra_frags_ring2",1163"d_rx_extra_frags_ring3",1164"d_rx_msdu_htt",1165"d_rx_mpdu_htt",1166"d_rx_msdu_stack",1167"d_rx_mpdu_stack",1168"d_rx_phy_err",1169"d_rx_phy_err_drops",1170"d_rx_mpdu_errors", /* FCS, MIC, ENC */1171"d_fw_crash_count",1172"d_fw_warm_reset_count",1173"d_fw_cold_reset_count",1174};11751176#define ATH10K_SSTATS_LEN ARRAY_SIZE(ath10k_gstrings_stats)11771178void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,1179struct ieee80211_vif *vif,1180u32 sset, u8 *data)1181{1182if (sset == ETH_SS_STATS)1183memcpy(data, *ath10k_gstrings_stats,1184sizeof(ath10k_gstrings_stats));1185}11861187int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,1188struct ieee80211_vif *vif, int sset)1189{1190if (sset == ETH_SS_STATS)1191return ATH10K_SSTATS_LEN;11921193return 0;1194}11951196void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,1197struct ieee80211_vif *vif,1198struct ethtool_stats *stats, u64 *data)1199{1200struct ath10k *ar = hw->priv;1201static const struct ath10k_fw_stats_pdev zero_stats = {};1202const struct ath10k_fw_stats_pdev *pdev_stats;1203int i = 0, ret;12041205mutex_lock(&ar->conf_mutex);12061207if (ar->state == ATH10K_STATE_ON) {1208ret = ath10k_debug_fw_stats_request(ar);1209if (ret) {1210/* just print a warning and try to use older results */1211ath10k_warn(ar,1212"failed to get fw stats for ethtool: %d\n",1213ret);1214}1215}12161217pdev_stats = list_first_entry_or_null(&ar->debug.fw_stats.pdevs,1218struct ath10k_fw_stats_pdev,1219list);1220if (!pdev_stats) {1221/* no results available so just return zeroes */1222pdev_stats = &zero_stats;1223}12241225spin_lock_bh(&ar->data_lock);12261227data[i++] = pdev_stats->hw_reaped; /* ppdu reaped */1228data[i++] = 0; /* tx bytes */1229data[i++] = pdev_stats->htt_mpdus;1230data[i++] = 0; /* rx bytes */1231data[i++] = pdev_stats->ch_noise_floor;1232data[i++] = pdev_stats->cycle_count;1233data[i++] = pdev_stats->phy_err_count;1234data[i++] = pdev_stats->rts_bad;1235data[i++] = pdev_stats->rts_good;1236data[i++] = pdev_stats->chan_tx_power;1237data[i++] = pdev_stats->fcs_bad;1238data[i++] = ar->stats.rx_crc_err_drop;1239data[i++] = pdev_stats->no_beacons;1240data[i++] = pdev_stats->mpdu_enqued;1241data[i++] = pdev_stats->msdu_enqued;1242data[i++] = pdev_stats->wmm_drop;1243data[i++] = pdev_stats->local_enqued;1244data[i++] = pdev_stats->local_freed;1245data[i++] = pdev_stats->hw_queued;1246data[i++] = pdev_stats->hw_reaped;1247data[i++] = pdev_stats->underrun;1248data[i++] = pdev_stats->tx_abort;1249data[i++] = pdev_stats->mpdus_requeued;1250data[i++] = pdev_stats->tx_ko;1251data[i++] = pdev_stats->data_rc;1252data[i++] = pdev_stats->sw_retry_failure;1253data[i++] = pdev_stats->illgl_rate_phy_err;1254data[i++] = pdev_stats->pdev_cont_xretry;1255data[i++] = pdev_stats->pdev_tx_timeout;1256data[i++] = pdev_stats->txop_ovf;1257data[i++] = pdev_stats->pdev_resets;1258data[i++] = pdev_stats->mid_ppdu_route_change;1259data[i++] = pdev_stats->status_rcvd;1260data[i++] = pdev_stats->r0_frags;1261data[i++] = pdev_stats->r1_frags;1262data[i++] = pdev_stats->r2_frags;1263data[i++] = pdev_stats->r3_frags;1264data[i++] = pdev_stats->htt_msdus;1265data[i++] = pdev_stats->htt_mpdus;1266data[i++] = pdev_stats->loc_msdus;1267data[i++] = pdev_stats->loc_mpdus;1268data[i++] = pdev_stats->phy_errs;1269data[i++] = pdev_stats->phy_err_drop;1270data[i++] = pdev_stats->mpdu_errs;1271data[i++] = ar->stats.fw_crash_counter;1272data[i++] = ar->stats.fw_warm_reset_counter;1273data[i++] = ar->stats.fw_cold_reset_counter;12741275spin_unlock_bh(&ar->data_lock);12761277mutex_unlock(&ar->conf_mutex);12781279WARN_ON(i != ATH10K_SSTATS_LEN);1280}12811282static const struct file_operations fops_fw_dbglog = {1283.read = ath10k_read_fw_dbglog,1284.write = ath10k_write_fw_dbglog,1285.open = simple_open,1286.owner = THIS_MODULE,1287.llseek = default_llseek,1288};12891290static int ath10k_debug_cal_data_fetch(struct ath10k *ar)1291{1292u32 hi_addr;1293__le32 addr;1294int ret;12951296lockdep_assert_held(&ar->conf_mutex);12971298if (WARN_ON(ar->hw_params.cal_data_len > ATH10K_DEBUG_CAL_DATA_LEN))1299return -EINVAL;13001301if (ar->hw_params.cal_data_len == 0)1302return -EOPNOTSUPP;13031304hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));13051306ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));1307if (ret) {1308ath10k_warn(ar, "failed to read hi_board_data address: %d\n",1309ret);1310return ret;1311}13121313ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), ar->debug.cal_data,1314ar->hw_params.cal_data_len);1315if (ret) {1316ath10k_warn(ar, "failed to read calibration data: %d\n", ret);1317return ret;1318}13191320return 0;1321}13221323static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)1324{1325struct ath10k *ar = inode->i_private;13261327mutex_lock(&ar->conf_mutex);13281329if (ar->state == ATH10K_STATE_ON ||1330ar->state == ATH10K_STATE_UTF) {1331ath10k_debug_cal_data_fetch(ar);1332}13331334file->private_data = ar;1335mutex_unlock(&ar->conf_mutex);13361337return 0;1338}13391340static ssize_t ath10k_debug_cal_data_read(struct file *file,1341char __user *user_buf,1342size_t count, loff_t *ppos)1343{1344struct ath10k *ar = file->private_data;13451346mutex_lock(&ar->conf_mutex);13471348count = simple_read_from_buffer(user_buf, count, ppos,1349ar->debug.cal_data,1350ar->hw_params.cal_data_len);13511352mutex_unlock(&ar->conf_mutex);13531354return count;1355}13561357static ssize_t ath10k_write_ani_enable(struct file *file,1358const char __user *user_buf,1359size_t count, loff_t *ppos)1360{1361struct ath10k *ar = file->private_data;1362int ret;1363u8 enable;13641365if (kstrtou8_from_user(user_buf, count, 0, &enable))1366return -EINVAL;13671368mutex_lock(&ar->conf_mutex);13691370if (ar->ani_enabled == enable) {1371ret = count;1372goto exit;1373}13741375ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->ani_enable,1376enable);1377if (ret) {1378ath10k_warn(ar, "ani_enable failed from debugfs: %d\n", ret);1379goto exit;1380}1381ar->ani_enabled = enable;13821383ret = count;13841385exit:1386mutex_unlock(&ar->conf_mutex);13871388return ret;1389}13901391static ssize_t ath10k_read_ani_enable(struct file *file, char __user *user_buf,1392size_t count, loff_t *ppos)1393{1394struct ath10k *ar = file->private_data;1395size_t len;1396char buf[32];13971398len = scnprintf(buf, sizeof(buf), "%d\n", ar->ani_enabled);13991400return simple_read_from_buffer(user_buf, count, ppos, buf, len);1401}14021403static const struct file_operations fops_ani_enable = {1404.read = ath10k_read_ani_enable,1405.write = ath10k_write_ani_enable,1406.open = simple_open,1407.owner = THIS_MODULE,1408.llseek = default_llseek,1409};14101411static const struct file_operations fops_cal_data = {1412.open = ath10k_debug_cal_data_open,1413.read = ath10k_debug_cal_data_read,1414.owner = THIS_MODULE,1415.llseek = default_llseek,1416};14171418static ssize_t ath10k_read_nf_cal_period(struct file *file,1419char __user *user_buf,1420size_t count, loff_t *ppos)1421{1422struct ath10k *ar = file->private_data;1423size_t len;1424char buf[32];14251426len = scnprintf(buf, sizeof(buf), "%d\n", ar->debug.nf_cal_period);14271428return simple_read_from_buffer(user_buf, count, ppos, buf, len);1429}14301431static ssize_t ath10k_write_nf_cal_period(struct file *file,1432const char __user *user_buf,1433size_t count, loff_t *ppos)1434{1435struct ath10k *ar = file->private_data;1436unsigned long period;1437int ret;14381439ret = kstrtoul_from_user(user_buf, count, 0, &period);1440if (ret)1441return ret;14421443if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX)1444return -EINVAL;14451446/* there's no way to switch back to the firmware default */1447if (period == 0)1448return -EINVAL;14491450mutex_lock(&ar->conf_mutex);14511452ar->debug.nf_cal_period = period;14531454if (ar->state != ATH10K_STATE_ON) {1455/* firmware is not running, nothing else to do */1456ret = count;1457goto exit;1458}14591460ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period,1461ar->debug.nf_cal_period);1462if (ret) {1463ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n",1464ret);1465goto exit;1466}14671468ret = count;14691470exit:1471mutex_unlock(&ar->conf_mutex);14721473return ret;1474}14751476static const struct file_operations fops_nf_cal_period = {1477.read = ath10k_read_nf_cal_period,1478.write = ath10k_write_nf_cal_period,1479.open = simple_open,1480.owner = THIS_MODULE,1481.llseek = default_llseek,1482};14831484#define ATH10K_TPC_CONFIG_BUF_SIZE (1024 * 1024)14851486static int ath10k_debug_tpc_stats_request(struct ath10k *ar)1487{1488int ret;1489unsigned long time_left;14901491lockdep_assert_held(&ar->conf_mutex);14921493reinit_completion(&ar->debug.tpc_complete);14941495ret = ath10k_wmi_pdev_get_tpc_config(ar, WMI_TPC_CONFIG_PARAM);1496if (ret) {1497ath10k_warn(ar, "failed to request tpc config: %d\n", ret);1498return ret;1499}15001501time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,15021 * HZ);1503if (time_left == 0)1504return -ETIMEDOUT;15051506return 0;1507}15081509void ath10k_debug_tpc_stats_process(struct ath10k *ar,1510struct ath10k_tpc_stats *tpc_stats)1511{1512spin_lock_bh(&ar->data_lock);15131514kfree(ar->debug.tpc_stats);1515ar->debug.tpc_stats = tpc_stats;1516complete(&ar->debug.tpc_complete);15171518spin_unlock_bh(&ar->data_lock);1519}15201521void1522ath10k_debug_tpc_stats_final_process(struct ath10k *ar,1523struct ath10k_tpc_stats_final *tpc_stats)1524{1525spin_lock_bh(&ar->data_lock);15261527kfree(ar->debug.tpc_stats_final);1528ar->debug.tpc_stats_final = tpc_stats;1529complete(&ar->debug.tpc_complete);15301531spin_unlock_bh(&ar->data_lock);1532}15331534static void ath10k_tpc_stats_print(struct ath10k_tpc_stats *tpc_stats,1535unsigned int j, char *buf, size_t *len)1536{1537int i;1538size_t buf_len;1539static const char table_str[][5] = { "CDD",1540"STBC",1541"TXBF" };1542static const char pream_str[][6] = { "CCK",1543"OFDM",1544"HT20",1545"HT40",1546"VHT20",1547"VHT40",1548"VHT80",1549"HTCUP" };15501551buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;1552*len += scnprintf(buf + *len, buf_len - *len,1553"********************************\n");1554*len += scnprintf(buf + *len, buf_len - *len,1555"******************* %s POWER TABLE ****************\n",1556table_str[j]);1557*len += scnprintf(buf + *len, buf_len - *len,1558"********************************\n");1559*len += scnprintf(buf + *len, buf_len - *len,1560"No. Preamble Rate_code ");15611562for (i = 0; i < tpc_stats->num_tx_chain; i++)1563*len += scnprintf(buf + *len, buf_len - *len,1564"tpc_value%d ", i);15651566*len += scnprintf(buf + *len, buf_len - *len, "\n");15671568for (i = 0; i < tpc_stats->rate_max; i++) {1569*len += scnprintf(buf + *len, buf_len - *len,1570"%8d %s 0x%2x %s\n", i,1571pream_str[tpc_stats->tpc_table[j].pream_idx[i]],1572tpc_stats->tpc_table[j].rate_code[i],1573tpc_stats->tpc_table[j].tpc_value[i]);1574}15751576*len += scnprintf(buf + *len, buf_len - *len,1577"***********************************\n");1578}15791580static void ath10k_tpc_stats_fill(struct ath10k *ar,1581struct ath10k_tpc_stats *tpc_stats,1582char *buf)1583{1584int j;1585size_t len, buf_len;15861587len = 0;1588buf_len = ATH10K_TPC_CONFIG_BUF_SIZE;15891590spin_lock_bh(&ar->data_lock);15911592if (!tpc_stats) {1593ath10k_warn(ar, "failed to get tpc stats\n");1594goto unlock;1595}15961597len += scnprintf(buf + len, buf_len - len, "\n");1598len += scnprintf(buf + len, buf_len - len,1599"*************************************\n");1600len += scnprintf(buf + len, buf_len - len,1601"TPC config for channel %4d mode %d\n",1602tpc_stats->chan_freq,1603tpc_stats->phy_mode);1604len += scnprintf(buf + len, buf_len - len,1605"*************************************\n");1606len += scnprintf(buf + len, buf_len - len,1607"CTL = 0x%2x Reg. Domain = %2d\n",1608tpc_stats->ctl,1609tpc_stats->reg_domain);1610len += scnprintf(buf + len, buf_len - len,1611"Antenna Gain = %2d Reg. Max Antenna Gain = %2d\n",1612tpc_stats->twice_antenna_gain,1613tpc_stats->twice_antenna_reduction);1614len += scnprintf(buf + len, buf_len - len,1615"Power Limit = %2d Reg. Max Power = %2d\n",1616tpc_stats->power_limit,1617tpc_stats->twice_max_rd_power / 2);1618len += scnprintf(buf + len, buf_len - len,1619"Num tx chains = %2d Num supported rates = %2d\n",1620tpc_stats->num_tx_chain,1621tpc_stats->rate_max);16221623for (j = 0; j < WMI_TPC_FLAG; j++) {1624switch (j) {1625case WMI_TPC_TABLE_TYPE_CDD:1626if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {1627len += scnprintf(buf + len, buf_len - len,1628"CDD not supported\n");1629break;1630}16311632ath10k_tpc_stats_print(tpc_stats, j, buf, &len);1633break;1634case WMI_TPC_TABLE_TYPE_STBC:1635if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {1636len += scnprintf(buf + len, buf_len - len,1637"STBC not supported\n");1638break;1639}16401641ath10k_tpc_stats_print(tpc_stats, j, buf, &len);1642break;1643case WMI_TPC_TABLE_TYPE_TXBF:1644if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) {1645len += scnprintf(buf + len, buf_len - len,1646"TXBF not supported\n***************************\n");1647break;1648}16491650ath10k_tpc_stats_print(tpc_stats, j, buf, &len);1651break;1652default:1653len += scnprintf(buf + len, buf_len - len,1654"Invalid Type\n");1655break;1656}1657}16581659unlock:1660spin_unlock_bh(&ar->data_lock);16611662if (len >= buf_len)1663buf[len - 1] = 0;1664else1665buf[len] = 0;1666}16671668static int ath10k_tpc_stats_open(struct inode *inode, struct file *file)1669{1670struct ath10k *ar = inode->i_private;1671void *buf = NULL;1672int ret;16731674mutex_lock(&ar->conf_mutex);16751676if (ar->state != ATH10K_STATE_ON) {1677ret = -ENETDOWN;1678goto err_unlock;1679}16801681buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);1682if (!buf) {1683ret = -ENOMEM;1684goto err_unlock;1685}16861687ret = ath10k_debug_tpc_stats_request(ar);1688if (ret) {1689ath10k_warn(ar, "failed to request tpc config stats: %d\n",1690ret);1691goto err_free;1692}16931694ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);1695file->private_data = buf;16961697mutex_unlock(&ar->conf_mutex);1698return 0;16991700err_free:1701vfree(buf);17021703err_unlock:1704mutex_unlock(&ar->conf_mutex);1705return ret;1706}17071708static int ath10k_tpc_stats_release(struct inode *inode, struct file *file)1709{1710vfree(file->private_data);17111712return 0;1713}17141715static ssize_t ath10k_tpc_stats_read(struct file *file, char __user *user_buf,1716size_t count, loff_t *ppos)1717{1718const char *buf = file->private_data;1719size_t len = strlen(buf);17201721return simple_read_from_buffer(user_buf, count, ppos, buf, len);1722}17231724static const struct file_operations fops_tpc_stats = {1725.open = ath10k_tpc_stats_open,1726.release = ath10k_tpc_stats_release,1727.read = ath10k_tpc_stats_read,1728.owner = THIS_MODULE,1729.llseek = default_llseek,1730};17311732int ath10k_debug_start(struct ath10k *ar)1733{1734int ret;17351736lockdep_assert_held(&ar->conf_mutex);17371738ret = ath10k_debug_htt_stats_req(ar);1739if (ret)1740/* continue normally anyway, this isn't serious */1741ath10k_warn(ar, "failed to start htt stats workqueue: %d\n",1742ret);17431744if (ar->debug.fw_dbglog_mask) {1745ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask,1746ATH10K_DBGLOG_LEVEL_WARN);1747if (ret)1748/* not serious */1749ath10k_warn(ar, "failed to enable dbglog during start: %d",1750ret);1751}17521753if (ar->pktlog_filter) {1754ret = ath10k_wmi_pdev_pktlog_enable(ar,1755ar->pktlog_filter);1756if (ret)1757/* not serious */1758ath10k_warn(ar,1759"failed to enable pktlog filter %x: %d\n",1760ar->pktlog_filter, ret);1761} else {1762ret = ath10k_wmi_pdev_pktlog_disable(ar);1763if (ret)1764/* not serious */1765ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);1766}17671768if (ar->debug.nf_cal_period &&1769!test_bit(ATH10K_FW_FEATURE_NON_BMI,1770ar->normal_mode_fw.fw_file.fw_features)) {1771ret = ath10k_wmi_pdev_set_param(ar,1772ar->wmi.pdev_param->cal_period,1773ar->debug.nf_cal_period);1774if (ret)1775/* not serious */1776ath10k_warn(ar, "cal period cfg failed from debug start: %d\n",1777ret);1778}17791780return ret;1781}17821783void ath10k_debug_stop(struct ath10k *ar)1784{1785lockdep_assert_held(&ar->conf_mutex);17861787if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,1788ar->normal_mode_fw.fw_file.fw_features))1789ath10k_debug_cal_data_fetch(ar);17901791/* Must not use _sync to avoid deadlock, we do that in1792* ath10k_debug_destroy(). The check for htt_stats_mask is to avoid1793* warning from del_timer().1794*/1795if (ar->debug.htt_stats_mask != 0)1796cancel_delayed_work(&ar->debug.htt_stats_dwork);17971798ath10k_wmi_pdev_pktlog_disable(ar);1799}18001801static ssize_t ath10k_write_simulate_radar(struct file *file,1802const char __user *user_buf,1803size_t count, loff_t *ppos)1804{1805struct ath10k *ar = file->private_data;1806struct ath10k_vif *arvif;18071808/* Just check for the first vif alone, as all the vifs will be1809* sharing the same channel and if the channel is disabled, all the1810* vifs will share the same 'is_started' state.1811*/1812arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list);1813if (!arvif->is_started)1814return -EINVAL;18151816ieee80211_radar_detected(ar->hw);18171818return count;1819}18201821static const struct file_operations fops_simulate_radar = {1822.write = ath10k_write_simulate_radar,1823.open = simple_open,1824.owner = THIS_MODULE,1825.llseek = default_llseek,1826};18271828#define ATH10K_DFS_STAT(s, p) (\1829len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \1830ar->debug.dfs_stats.p))18311832#define ATH10K_DFS_POOL_STAT(s, p) (\1833len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \1834ar->debug.dfs_pool_stats.p))18351836static ssize_t ath10k_read_dfs_stats(struct file *file, char __user *user_buf,1837size_t count, loff_t *ppos)1838{1839int retval = 0, len = 0;1840const int size = 8000;1841struct ath10k *ar = file->private_data;1842char *buf;18431844buf = kzalloc(size, GFP_KERNEL);1845if (buf == NULL)1846return -ENOMEM;18471848if (!ar->dfs_detector) {1849len += scnprintf(buf + len, size - len, "DFS not enabled\n");1850goto exit;1851}18521853ar->debug.dfs_pool_stats =1854ar->dfs_detector->get_stats(ar->dfs_detector);18551856len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n");18571858ATH10K_DFS_STAT("reported phy errors", phy_errors);1859ATH10K_DFS_STAT("pulse events reported", pulses_total);1860ATH10K_DFS_STAT("DFS pulses detected", pulses_detected);1861ATH10K_DFS_STAT("DFS pulses discarded", pulses_discarded);1862ATH10K_DFS_STAT("Radars detected", radar_detected);18631864len += scnprintf(buf + len, size - len, "Global Pool statistics:\n");1865ATH10K_DFS_POOL_STAT("Pool references", pool_reference);1866ATH10K_DFS_POOL_STAT("Pulses allocated", pulse_allocated);1867ATH10K_DFS_POOL_STAT("Pulses alloc error", pulse_alloc_error);1868ATH10K_DFS_POOL_STAT("Pulses in use", pulse_used);1869ATH10K_DFS_POOL_STAT("Seqs. allocated", pseq_allocated);1870ATH10K_DFS_POOL_STAT("Seqs. alloc error", pseq_alloc_error);1871ATH10K_DFS_POOL_STAT("Seqs. in use", pseq_used);18721873exit:1874if (len > size)1875len = size;18761877retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);1878kfree(buf);18791880return retval;1881}18821883static const struct file_operations fops_dfs_stats = {1884.read = ath10k_read_dfs_stats,1885.open = simple_open,1886.owner = THIS_MODULE,1887.llseek = default_llseek,1888};18891890static ssize_t ath10k_write_pktlog_filter(struct file *file,1891const char __user *ubuf,1892size_t count, loff_t *ppos)1893{1894struct ath10k *ar = file->private_data;1895u32 filter;1896int ret;18971898if (kstrtouint_from_user(ubuf, count, 0, &filter))1899return -EINVAL;19001901mutex_lock(&ar->conf_mutex);19021903if (ar->state != ATH10K_STATE_ON) {1904ar->pktlog_filter = filter;1905ret = count;1906goto out;1907}19081909if (filter == ar->pktlog_filter) {1910ret = count;1911goto out;1912}19131914if (filter) {1915ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);1916if (ret) {1917ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",1918ar->pktlog_filter, ret);1919goto out;1920}1921} else {1922ret = ath10k_wmi_pdev_pktlog_disable(ar);1923if (ret) {1924ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);1925goto out;1926}1927}19281929ar->pktlog_filter = filter;1930ret = count;19311932out:1933mutex_unlock(&ar->conf_mutex);1934return ret;1935}19361937static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,1938size_t count, loff_t *ppos)1939{1940char buf[32];1941struct ath10k *ar = file->private_data;1942int len = 0;19431944mutex_lock(&ar->conf_mutex);1945len = scnprintf(buf, sizeof(buf) - len, "%08x\n",1946ar->pktlog_filter);1947mutex_unlock(&ar->conf_mutex);19481949return simple_read_from_buffer(ubuf, count, ppos, buf, len);1950}19511952static const struct file_operations fops_pktlog_filter = {1953.read = ath10k_read_pktlog_filter,1954.write = ath10k_write_pktlog_filter,1955.open = simple_open1956};19571958static ssize_t ath10k_write_quiet_period(struct file *file,1959const char __user *ubuf,1960size_t count, loff_t *ppos)1961{1962struct ath10k *ar = file->private_data;1963u32 period;19641965if (kstrtouint_from_user(ubuf, count, 0, &period))1966return -EINVAL;19671968if (period < ATH10K_QUIET_PERIOD_MIN) {1969ath10k_warn(ar, "Quiet period %u can not be lesser than 25ms\n",1970period);1971return -EINVAL;1972}1973mutex_lock(&ar->conf_mutex);1974ar->thermal.quiet_period = period;1975ath10k_thermal_set_throttling(ar);1976mutex_unlock(&ar->conf_mutex);19771978return count;1979}19801981static ssize_t ath10k_read_quiet_period(struct file *file, char __user *ubuf,1982size_t count, loff_t *ppos)1983{1984char buf[32];1985struct ath10k *ar = file->private_data;1986int len = 0;19871988mutex_lock(&ar->conf_mutex);1989len = scnprintf(buf, sizeof(buf) - len, "%d\n",1990ar->thermal.quiet_period);1991mutex_unlock(&ar->conf_mutex);19921993return simple_read_from_buffer(ubuf, count, ppos, buf, len);1994}19951996static const struct file_operations fops_quiet_period = {1997.read = ath10k_read_quiet_period,1998.write = ath10k_write_quiet_period,1999.open = simple_open2000};20012002static ssize_t ath10k_write_btcoex(struct file *file,2003const char __user *ubuf,2004size_t count, loff_t *ppos)2005{2006struct ath10k *ar = file->private_data;2007char buf[32];2008size_t buf_size;2009int ret;2010bool val;2011u32 pdev_param;20122013buf_size = min(count, (sizeof(buf) - 1));2014if (copy_from_user(buf, ubuf, buf_size))2015return -EFAULT;20162017buf[buf_size] = '\0';20182019if (kstrtobool(buf, &val) != 0)2020return -EINVAL;20212022if (!ar->coex_support)2023return -EOPNOTSUPP;20242025mutex_lock(&ar->conf_mutex);20262027if (ar->state != ATH10K_STATE_ON &&2028ar->state != ATH10K_STATE_RESTARTED) {2029ret = -ENETDOWN;2030goto exit;2031}20322033if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val)) {2034ret = count;2035goto exit;2036}20372038pdev_param = ar->wmi.pdev_param->enable_btcoex;2039if (test_bit(ATH10K_FW_FEATURE_BTCOEX_PARAM,2040ar->running_fw->fw_file.fw_features)) {2041ret = ath10k_wmi_pdev_set_param(ar, pdev_param, val);2042if (ret) {2043ath10k_warn(ar, "failed to enable btcoex: %d\n", ret);2044ret = count;2045goto exit;2046}2047} else {2048ath10k_info(ar, "restarting firmware due to btcoex change");2049ath10k_core_start_recovery(ar);2050}20512052if (val)2053set_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);2054else2055clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);20562057ret = count;20582059exit:2060mutex_unlock(&ar->conf_mutex);20612062return ret;2063}20642065static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf,2066size_t count, loff_t *ppos)2067{2068char buf[32];2069struct ath10k *ar = file->private_data;2070int len = 0;20712072mutex_lock(&ar->conf_mutex);2073len = scnprintf(buf, sizeof(buf) - len, "%d\n",2074test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags));2075mutex_unlock(&ar->conf_mutex);20762077return simple_read_from_buffer(ubuf, count, ppos, buf, len);2078}20792080static const struct file_operations fops_btcoex = {2081.read = ath10k_read_btcoex,2082.write = ath10k_write_btcoex,2083.open = simple_open2084};20852086static ssize_t ath10k_write_enable_extd_tx_stats(struct file *file,2087const char __user *ubuf,2088size_t count, loff_t *ppos)2089{2090struct ath10k *ar = file->private_data;2091u32 filter;2092int ret;20932094if (kstrtouint_from_user(ubuf, count, 0, &filter))2095return -EINVAL;20962097mutex_lock(&ar->conf_mutex);20982099if (ar->state != ATH10K_STATE_ON) {2100ar->debug.enable_extd_tx_stats = filter;2101ret = count;2102goto out;2103}21042105if (filter == ar->debug.enable_extd_tx_stats) {2106ret = count;2107goto out;2108}21092110ar->debug.enable_extd_tx_stats = filter;2111ret = count;21122113out:2114mutex_unlock(&ar->conf_mutex);2115return ret;2116}21172118static ssize_t ath10k_read_enable_extd_tx_stats(struct file *file,2119char __user *ubuf,2120size_t count, loff_t *ppos)21212122{2123char buf[32];2124struct ath10k *ar = file->private_data;2125int len = 0;21262127mutex_lock(&ar->conf_mutex);2128len = scnprintf(buf, sizeof(buf) - len, "%08x\n",2129ar->debug.enable_extd_tx_stats);2130mutex_unlock(&ar->conf_mutex);21312132return simple_read_from_buffer(ubuf, count, ppos, buf, len);2133}21342135static const struct file_operations fops_enable_extd_tx_stats = {2136.read = ath10k_read_enable_extd_tx_stats,2137.write = ath10k_write_enable_extd_tx_stats,2138.open = simple_open2139};21402141static ssize_t ath10k_write_peer_stats(struct file *file,2142const char __user *ubuf,2143size_t count, loff_t *ppos)2144{2145struct ath10k *ar = file->private_data;2146char buf[32];2147size_t buf_size;2148int ret;2149bool val;21502151buf_size = min(count, (sizeof(buf) - 1));2152if (copy_from_user(buf, ubuf, buf_size))2153return -EFAULT;21542155buf[buf_size] = '\0';21562157if (kstrtobool(buf, &val) != 0)2158return -EINVAL;21592160mutex_lock(&ar->conf_mutex);21612162if (ar->state != ATH10K_STATE_ON &&2163ar->state != ATH10K_STATE_RESTARTED) {2164ret = -ENETDOWN;2165goto exit;2166}21672168if (!(test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags) ^ val)) {2169ret = count;2170goto exit;2171}21722173if (val)2174set_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);2175else2176clear_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);21772178ath10k_info(ar, "restarting firmware due to Peer stats change");21792180ath10k_core_start_recovery(ar);2181ret = count;21822183exit:2184mutex_unlock(&ar->conf_mutex);2185return ret;2186}21872188static ssize_t ath10k_read_peer_stats(struct file *file, char __user *ubuf,2189size_t count, loff_t *ppos)21902191{2192char buf[32];2193struct ath10k *ar = file->private_data;2194int len = 0;21952196mutex_lock(&ar->conf_mutex);2197len = scnprintf(buf, sizeof(buf) - len, "%d\n",2198test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags));2199mutex_unlock(&ar->conf_mutex);22002201return simple_read_from_buffer(ubuf, count, ppos, buf, len);2202}22032204static const struct file_operations fops_peer_stats = {2205.read = ath10k_read_peer_stats,2206.write = ath10k_write_peer_stats,2207.open = simple_open2208};22092210static ssize_t ath10k_debug_fw_checksums_read(struct file *file,2211char __user *user_buf,2212size_t count, loff_t *ppos)2213{2214struct ath10k *ar = file->private_data;2215size_t len = 0, buf_len = 4096;2216ssize_t ret_cnt;2217char *buf;22182219buf = kzalloc(buf_len, GFP_KERNEL);2220if (!buf)2221return -ENOMEM;22222223mutex_lock(&ar->conf_mutex);22242225len += scnprintf(buf + len, buf_len - len,2226"firmware-N.bin\t\t%08x\n",2227crc32_le(0, ar->normal_mode_fw.fw_file.firmware->data,2228ar->normal_mode_fw.fw_file.firmware->size));2229len += scnprintf(buf + len, buf_len - len,2230"athwlan\t\t\t%08x\n",2231crc32_le(0, ar->normal_mode_fw.fw_file.firmware_data,2232ar->normal_mode_fw.fw_file.firmware_len));2233len += scnprintf(buf + len, buf_len - len,2234"otp\t\t\t%08x\n",2235crc32_le(0, ar->normal_mode_fw.fw_file.otp_data,2236ar->normal_mode_fw.fw_file.otp_len));2237len += scnprintf(buf + len, buf_len - len,2238"codeswap\t\t%08x\n",2239crc32_le(0, ar->normal_mode_fw.fw_file.codeswap_data,2240ar->normal_mode_fw.fw_file.codeswap_len));2241len += scnprintf(buf + len, buf_len - len,2242"board-N.bin\t\t%08x\n",2243crc32_le(0, ar->normal_mode_fw.board->data,2244ar->normal_mode_fw.board->size));2245len += scnprintf(buf + len, buf_len - len,2246"board\t\t\t%08x\n",2247crc32_le(0, ar->normal_mode_fw.board_data,2248ar->normal_mode_fw.board_len));22492250ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);22512252mutex_unlock(&ar->conf_mutex);22532254kfree(buf);2255return ret_cnt;2256}22572258static const struct file_operations fops_fw_checksums = {2259.read = ath10k_debug_fw_checksums_read,2260.open = simple_open,2261.owner = THIS_MODULE,2262.llseek = default_llseek,2263};22642265static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file,2266char __user *user_buf,2267size_t count, loff_t *ppos)2268{2269struct ath10k *ar = file->private_data;2270char buf[32];2271size_t len;22722273len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask);2274return simple_read_from_buffer(user_buf, count, ppos, buf, len);2275}22762277static ssize_t ath10k_sta_tid_stats_mask_write(struct file *file,2278const char __user *user_buf,2279size_t count, loff_t *ppos)2280{2281struct ath10k *ar = file->private_data;2282char buf[32];2283ssize_t len;2284u32 mask;22852286len = min(count, sizeof(buf) - 1);2287if (copy_from_user(buf, user_buf, len))2288return -EFAULT;22892290buf[len] = '\0';2291if (kstrtoint(buf, 0, &mask))2292return -EINVAL;22932294ar->sta_tid_stats_mask = mask;22952296return len;2297}22982299static const struct file_operations fops_sta_tid_stats_mask = {2300.read = ath10k_sta_tid_stats_mask_read,2301.write = ath10k_sta_tid_stats_mask_write,2302.open = simple_open,2303.owner = THIS_MODULE,2304.llseek = default_llseek,2305};23062307static int ath10k_debug_tpc_stats_final_request(struct ath10k *ar)2308{2309int ret;2310unsigned long time_left;23112312lockdep_assert_held(&ar->conf_mutex);23132314reinit_completion(&ar->debug.tpc_complete);23152316ret = ath10k_wmi_pdev_get_tpc_table_cmdid(ar, WMI_TPC_CONFIG_PARAM);2317if (ret) {2318ath10k_warn(ar, "failed to request tpc table cmdid: %d\n", ret);2319return ret;2320}23212322time_left = wait_for_completion_timeout(&ar->debug.tpc_complete,23231 * HZ);2324if (time_left == 0)2325return -ETIMEDOUT;23262327return 0;2328}23292330static int ath10k_tpc_stats_final_open(struct inode *inode, struct file *file)2331{2332struct ath10k *ar = inode->i_private;2333void *buf;2334int ret;23352336mutex_lock(&ar->conf_mutex);23372338if (ar->state != ATH10K_STATE_ON) {2339ret = -ENETDOWN;2340goto err_unlock;2341}23422343buf = vmalloc(ATH10K_TPC_CONFIG_BUF_SIZE);2344if (!buf) {2345ret = -ENOMEM;2346goto err_unlock;2347}23482349ret = ath10k_debug_tpc_stats_final_request(ar);2350if (ret) {2351ath10k_warn(ar, "failed to request tpc stats final: %d\n",2352ret);2353goto err_free;2354}23552356ath10k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf);2357file->private_data = buf;23582359mutex_unlock(&ar->conf_mutex);2360return 0;23612362err_free:2363vfree(buf);23642365err_unlock:2366mutex_unlock(&ar->conf_mutex);2367return ret;2368}23692370static int ath10k_tpc_stats_final_release(struct inode *inode,2371struct file *file)2372{2373vfree(file->private_data);23742375return 0;2376}23772378static ssize_t ath10k_tpc_stats_final_read(struct file *file,2379char __user *user_buf,2380size_t count, loff_t *ppos)2381{2382const char *buf = file->private_data;2383unsigned int len = strlen(buf);23842385return simple_read_from_buffer(user_buf, count, ppos, buf, len);2386}23872388static const struct file_operations fops_tpc_stats_final = {2389.open = ath10k_tpc_stats_final_open,2390.release = ath10k_tpc_stats_final_release,2391.read = ath10k_tpc_stats_final_read,2392.owner = THIS_MODULE,2393.llseek = default_llseek,2394};23952396static ssize_t ath10k_write_warm_hw_reset(struct file *file,2397const char __user *user_buf,2398size_t count, loff_t *ppos)2399{2400struct ath10k *ar = file->private_data;2401int ret;2402bool val;24032404if (kstrtobool_from_user(user_buf, count, &val))2405return -EFAULT;24062407if (!val)2408return -EINVAL;24092410mutex_lock(&ar->conf_mutex);24112412if (ar->state != ATH10K_STATE_ON) {2413ret = -ENETDOWN;2414goto exit;2415}24162417ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pdev_reset,2418WMI_RST_MODE_WARM_RESET);24192420if (ret) {2421ath10k_warn(ar, "failed to enable warm hw reset: %d\n", ret);2422goto exit;2423}24242425ret = count;24262427exit:2428mutex_unlock(&ar->conf_mutex);2429return ret;2430}24312432static const struct file_operations fops_warm_hw_reset = {2433.write = ath10k_write_warm_hw_reset,2434.open = simple_open,2435.owner = THIS_MODULE,2436.llseek = default_llseek,2437};24382439static void ath10k_peer_ps_state_disable(void *data,2440struct ieee80211_sta *sta)2441{2442struct ath10k *ar = data;2443struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;24442445spin_lock_bh(&ar->data_lock);2446arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;2447spin_unlock_bh(&ar->data_lock);2448}24492450static ssize_t ath10k_write_ps_state_enable(struct file *file,2451const char __user *user_buf,2452size_t count, loff_t *ppos)2453{2454struct ath10k *ar = file->private_data;2455int ret;2456u32 param;2457u8 ps_state_enable;24582459if (kstrtou8_from_user(user_buf, count, 0, &ps_state_enable))2460return -EINVAL;24612462if (ps_state_enable > 1)2463return -EINVAL;24642465mutex_lock(&ar->conf_mutex);24662467if (ar->ps_state_enable == ps_state_enable) {2468ret = count;2469goto exit;2470}24712472param = ar->wmi.pdev_param->peer_sta_ps_statechg_enable;2473ret = ath10k_wmi_pdev_set_param(ar, param, ps_state_enable);2474if (ret) {2475ath10k_warn(ar, "failed to enable ps_state_enable: %d\n",2476ret);2477goto exit;2478}2479ar->ps_state_enable = ps_state_enable;24802481if (!ar->ps_state_enable)2482ieee80211_iterate_stations_atomic(ar->hw,2483ath10k_peer_ps_state_disable,2484ar);24852486ret = count;24872488exit:2489mutex_unlock(&ar->conf_mutex);24902491return ret;2492}24932494static ssize_t ath10k_read_ps_state_enable(struct file *file,2495char __user *user_buf,2496size_t count, loff_t *ppos)2497{2498struct ath10k *ar = file->private_data;2499int len = 0;2500char buf[32];25012502mutex_lock(&ar->conf_mutex);2503len = scnprintf(buf, sizeof(buf) - len, "%d\n",2504ar->ps_state_enable);2505mutex_unlock(&ar->conf_mutex);25062507return simple_read_from_buffer(user_buf, count, ppos, buf, len);2508}25092510static const struct file_operations fops_ps_state_enable = {2511.read = ath10k_read_ps_state_enable,2512.write = ath10k_write_ps_state_enable,2513.open = simple_open,2514.owner = THIS_MODULE,2515.llseek = default_llseek,2516};25172518static ssize_t ath10k_write_reset_htt_stats(struct file *file,2519const char __user *user_buf,2520size_t count, loff_t *ppos)2521{2522struct ath10k *ar = file->private_data;2523unsigned long reset;2524int ret;25252526ret = kstrtoul_from_user(user_buf, count, 0, &reset);2527if (ret)2528return ret;25292530if (reset == 0 || reset > 0x1ffff)2531return -EINVAL;25322533mutex_lock(&ar->conf_mutex);25342535ar->debug.reset_htt_stats = reset;25362537ret = ath10k_debug_htt_stats_req(ar);2538if (ret)2539goto out;25402541ar->debug.reset_htt_stats = 0;2542ret = count;25432544out:2545mutex_unlock(&ar->conf_mutex);2546return ret;2547}25482549static const struct file_operations fops_reset_htt_stats = {2550.write = ath10k_write_reset_htt_stats,2551.owner = THIS_MODULE,2552.open = simple_open,2553.llseek = default_llseek,2554};25552556int ath10k_debug_create(struct ath10k *ar)2557{2558ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN);2559if (!ar->debug.cal_data)2560return -ENOMEM;25612562INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);2563INIT_LIST_HEAD(&ar->debug.fw_stats.vdevs);2564INIT_LIST_HEAD(&ar->debug.fw_stats.peers);2565INIT_LIST_HEAD(&ar->debug.fw_stats.peers_extd);25662567return 0;2568}25692570void ath10k_debug_destroy(struct ath10k *ar)2571{2572vfree(ar->debug.cal_data);2573ar->debug.cal_data = NULL;25742575ath10k_debug_fw_stats_reset(ar);25762577kfree(ar->debug.tpc_stats);2578kfree(ar->debug.tpc_stats_final);2579}25802581int ath10k_debug_register(struct ath10k *ar)2582{2583ar->debug.debugfs_phy = debugfs_create_dir("ath10k",2584ar->hw->wiphy->debugfsdir);2585if (IS_ERR_OR_NULL(ar->debug.debugfs_phy)) {2586if (IS_ERR(ar->debug.debugfs_phy))2587return PTR_ERR(ar->debug.debugfs_phy);25882589return -ENOMEM;2590}25912592INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,2593ath10k_debug_htt_stats_dwork);25942595init_completion(&ar->debug.tpc_complete);2596init_completion(&ar->debug.fw_stats_complete);25972598debugfs_create_file("fw_stats", 0400, ar->debug.debugfs_phy, ar,2599&fops_fw_stats);26002601debugfs_create_file("fw_reset_stats", 0400, ar->debug.debugfs_phy, ar,2602&fops_fw_reset_stats);26032604debugfs_create_file("wmi_services", 0400, ar->debug.debugfs_phy, ar,2605&fops_wmi_services);26062607debugfs_create_file("simulate_fw_crash", 0600, ar->debug.debugfs_phy, ar,2608&fops_simulate_fw_crash);26092610debugfs_create_file("reg_addr", 0600, ar->debug.debugfs_phy, ar,2611&fops_reg_addr);26122613debugfs_create_file("reg_value", 0600, ar->debug.debugfs_phy, ar,2614&fops_reg_value);26152616debugfs_create_file("mem_value", 0600, ar->debug.debugfs_phy, ar,2617&fops_mem_value);26182619debugfs_create_file("chip_id", 0400, ar->debug.debugfs_phy, ar,2620&fops_chip_id);26212622debugfs_create_file("htt_stats_mask", 0600, ar->debug.debugfs_phy, ar,2623&fops_htt_stats_mask);26242625debugfs_create_file("htt_max_amsdu_ampdu", 0600, ar->debug.debugfs_phy, ar,2626&fops_htt_max_amsdu_ampdu);26272628debugfs_create_file("fw_dbglog", 0600, ar->debug.debugfs_phy, ar,2629&fops_fw_dbglog);26302631if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,2632ar->normal_mode_fw.fw_file.fw_features)) {2633debugfs_create_file("cal_data", 0400, ar->debug.debugfs_phy, ar,2634&fops_cal_data);26352636debugfs_create_file("nf_cal_period", 0600, ar->debug.debugfs_phy, ar,2637&fops_nf_cal_period);2638}26392640debugfs_create_file("ani_enable", 0600, ar->debug.debugfs_phy, ar,2641&fops_ani_enable);26422643if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) {2644debugfs_create_file("dfs_simulate_radar", 0200, ar->debug.debugfs_phy,2645ar, &fops_simulate_radar);26462647debugfs_create_bool("dfs_block_radar_events", 0200,2648ar->debug.debugfs_phy,2649&ar->dfs_block_radar_events);26502651debugfs_create_file("dfs_stats", 0400, ar->debug.debugfs_phy, ar,2652&fops_dfs_stats);2653}26542655debugfs_create_file("pktlog_filter", 0644, ar->debug.debugfs_phy, ar,2656&fops_pktlog_filter);26572658if (test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))2659debugfs_create_file("quiet_period", 0644, ar->debug.debugfs_phy, ar,2660&fops_quiet_period);26612662debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_phy, ar,2663&fops_tpc_stats);26642665if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))2666debugfs_create_file("btcoex", 0644, ar->debug.debugfs_phy, ar,2667&fops_btcoex);26682669if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {2670debugfs_create_file("peer_stats", 0644, ar->debug.debugfs_phy, ar,2671&fops_peer_stats);26722673debugfs_create_file("enable_extd_tx_stats", 0644,2674ar->debug.debugfs_phy, ar,2675&fops_enable_extd_tx_stats);2676}26772678debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar,2679&fops_fw_checksums);26802681if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS))2682debugfs_create_file("sta_tid_stats_mask", 0600,2683ar->debug.debugfs_phy,2684ar, &fops_sta_tid_stats_mask);26852686if (test_bit(WMI_SERVICE_TPC_STATS_FINAL, ar->wmi.svc_map))2687debugfs_create_file("tpc_stats_final", 0400,2688ar->debug.debugfs_phy, ar,2689&fops_tpc_stats_final);26902691if (test_bit(WMI_SERVICE_RESET_CHIP, ar->wmi.svc_map))2692debugfs_create_file("warm_hw_reset", 0600,2693ar->debug.debugfs_phy, ar,2694&fops_warm_hw_reset);26952696debugfs_create_file("ps_state_enable", 0600, ar->debug.debugfs_phy, ar,2697&fops_ps_state_enable);26982699debugfs_create_file("reset_htt_stats", 0200, ar->debug.debugfs_phy, ar,2700&fops_reset_htt_stats);27012702return 0;2703}27042705void ath10k_debug_unregister(struct ath10k *ar)2706{2707cancel_delayed_work_sync(&ar->debug.htt_stats_dwork);2708}27092710#endif /* CONFIG_ATH10K_DEBUGFS */27112712#ifdef CONFIG_ATH10K_DEBUG2713void __ath10k_dbg(struct ath10k *ar, enum ath10k_debug_mask mask,2714const char *fmt, ...)2715{2716struct va_format vaf;2717va_list args;27182719va_start(args, fmt);27202721vaf.fmt = fmt;2722vaf.va = &args;27232724if (ath10k_debug_mask & mask)2725#if defined(__linux__)2726dev_printk(KERN_DEBUG, ar->dev, "%pV", &vaf);2727#elif defined(__FreeBSD__)2728{2729char *str;2730vasprintf(&str, M_KMALLOC, fmt, args);2731dev_printk(KERN_DEBUG, ar->dev, "%s", str);2732free(str, M_KMALLOC);2733}2734#endif27352736trace_ath10k_log_dbg(ar, mask, &vaf);27372738va_end(args);2739}2740EXPORT_SYMBOL(__ath10k_dbg);27412742void ath10k_dbg_dump(struct ath10k *ar,2743enum ath10k_debug_mask mask,2744const char *msg, const char *prefix,2745const void *buf, size_t len)2746{2747#if defined(__linux__)2748char linebuf[256];2749size_t linebuflen;2750const void *ptr;2751#elif defined(__FreeBSD__)2752struct sbuf *sb;2753int rc;2754#endif27552756if (ath10k_debug_mask & mask) {2757if (msg)2758__ath10k_dbg(ar, mask, "%s\n", msg);27592760#if defined(__linux__)2761for (ptr = buf; (ptr - buf) < len; ptr += 16) {2762linebuflen = 0;2763linebuflen += scnprintf(linebuf + linebuflen,2764sizeof(linebuf) - linebuflen,2765"%s%08x: ",2766(prefix ? prefix : ""),2767(unsigned int)(ptr - buf));2768hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,2769linebuf + linebuflen,2770sizeof(linebuf) - linebuflen, true);2771dev_printk(KERN_DEBUG, ar->dev, "%s\n", linebuf);2772}2773#elif defined(__FreeBSD__)2774sb = sbuf_new_auto();2775if (sb == NULL)2776goto trace;27772778sbuf_hexdump(sb, buf, len, prefix, 0);2779sbuf_trim(sb);2780rc = sbuf_finish(sb);2781if (rc == 0)2782dev_printk(KERN_DEBUG, ar->dev, "%s\n", sbuf_data(sb));2783sbuf_delete(sb);2784trace: ;2785#endif2786}27872788/* tracing code doesn't like null strings :/ */2789trace_ath10k_log_dbg_dump(ar, msg ? msg : "", prefix ? prefix : "",2790buf, len);2791}2792EXPORT_SYMBOL(ath10k_dbg_dump);27932794#endif /* CONFIG_ATH10K_DEBUG */279527962797