summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath10k/debug.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/debug.c')
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c503
1 files changed, 503 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
new file mode 100644
index 000000000000..499034b873d1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2005-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include "core.h"
+#include "debug.h"
+
+static int ath10k_printk(const char *level, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int rtn;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ rtn = printk("%sath10k: %pV", level, &vaf);
+
+ va_end(args);
+
+ return rtn;
+}
+
+int ath10k_info(const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = ath10k_printk(KERN_INFO, "%pV", &vaf);
+ trace_ath10k_log_info(&vaf);
+ va_end(args);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath10k_info);
+
+int ath10k_err(const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = ath10k_printk(KERN_ERR, "%pV", &vaf);
+ trace_ath10k_log_err(&vaf);
+ va_end(args);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath10k_err);
+
+int ath10k_warn(const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret = 0;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+
+ if (net_ratelimit())
+ ret = ath10k_printk(KERN_WARNING, "%pV", &vaf);
+
+ trace_ath10k_log_warn(&vaf);
+
+ va_end(args);
+
+ return ret;
+}
+EXPORT_SYMBOL(ath10k_warn);
+
+#ifdef CONFIG_ATH10K_DEBUGFS
+
+void ath10k_debug_read_service_map(struct ath10k *ar,
+ void *service_map,
+ size_t map_size)
+{
+ memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
+}
+
+static ssize_t ath10k_read_wmi_services(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char *buf;
+ unsigned int len = 0, buf_len = 1500;
+ const char *status;
+ ssize_t ret_cnt;
+ int i;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (len > buf_len)
+ len = buf_len;
+
+ for (i = 0; i < WMI_SERVICE_LAST; i++) {
+ if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i))
+ status = "enabled";
+ else
+ status = "disabled";
+
+ len += scnprintf(buf + len, buf_len - len,
+ "0x%02x - %20s - %s\n",
+ i, wmi_service_name(i), status);
+ }
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_wmi_services = {
+ .read = ath10k_read_wmi_services,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+void ath10k_debug_read_target_stats(struct ath10k *ar,
+ struct wmi_stats_event *ev)
+{
+ u8 *tmp = ev->data;
+ struct ath10k_target_stats *stats;
+ int num_pdev_stats, num_vdev_stats, num_peer_stats;
+ struct wmi_pdev_stats *ps;
+ int i;
+
+ mutex_lock(&ar->conf_mutex);
+
+ stats = &ar->debug.target_stats;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
+
+ if (num_pdev_stats) {
+ ps = (struct wmi_pdev_stats *)tmp;
+
+ stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
+ stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
+ stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
+ stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
+ stats->cycle_count = __le32_to_cpu(ps->cycle_count);
+ stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
+ stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
+
+ stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
+ stats->comp_delivered =
+ __le32_to_cpu(ps->wal.tx.comp_delivered);
+ stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
+ stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
+ stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
+ stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
+ stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
+ stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
+ stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
+ stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
+ stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
+ stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
+ stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
+ stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
+ stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
+ stats->sw_retry_failure =
+ __le32_to_cpu(ps->wal.tx.sw_retry_failure);
+ stats->illgl_rate_phy_err =
+ __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
+ stats->pdev_cont_xretry =
+ __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
+ stats->pdev_tx_timeout =
+ __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
+ stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
+ stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
+ stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
+
+ stats->mid_ppdu_route_change =
+ __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
+ stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
+ stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
+ stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
+ stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
+ stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
+ stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
+ stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
+ stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
+ stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
+ stats->oversize_amsdu =
+ __le32_to_cpu(ps->wal.rx.oversize_amsdu);
+ stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
+ stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
+ stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
+
+ tmp += sizeof(struct wmi_pdev_stats);
+ }
+
+ /* 0 or max vdevs */
+ /* Currently firmware does not support VDEV stats */
+ if (num_vdev_stats) {
+ struct wmi_vdev_stats *vdev_stats;
+
+ for (i = 0; i < num_vdev_stats; i++) {
+ vdev_stats = (struct wmi_vdev_stats *)tmp;
+ tmp += sizeof(struct wmi_vdev_stats);
+ }
+ }
+
+ if (num_peer_stats) {
+ struct wmi_peer_stats *peer_stats;
+ struct ath10k_peer_stat *s;
+
+ stats->peers = num_peer_stats;
+
+ for (i = 0; i < num_peer_stats; i++) {
+ peer_stats = (struct wmi_peer_stats *)tmp;
+ s = &stats->peer_stat[i];
+
+ WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr,
+ s->peer_macaddr);
+ s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
+ s->peer_tx_rate =
+ __le32_to_cpu(peer_stats->peer_tx_rate);
+
+ tmp += sizeof(struct wmi_peer_stats);
+ }
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+ complete(&ar->debug.event_stats_compl);
+}
+
+static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ struct ath10k_target_stats *fw_stats;
+ char *buf;
+ unsigned int len = 0, buf_len = 2500;
+ ssize_t ret_cnt;
+ long left;
+ int i;
+ int ret;
+
+ fw_stats = &ar->debug.target_stats;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+ if (ret) {
+ ath10k_warn("could not request stats (%d)\n", ret);
+ kfree(buf);
+ return -EIO;
+ }
+
+ left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+
+ if (left <= 0) {
+ kfree(buf);
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&ar->conf_mutex);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath10k PDEV stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Channel noise floor", fw_stats->ch_noise_floor);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Channel TX power", fw_stats->chan_tx_power);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "TX frame count", fw_stats->tx_frame_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "RX frame count", fw_stats->rx_frame_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "RX clear count", fw_stats->rx_clear_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "Cycle count", fw_stats->cycle_count);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
+ "PHY error count", fw_stats->phy_err_count);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath10k PDEV TX stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HTT cookies queued", fw_stats->comp_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HTT cookies disp.", fw_stats->comp_delivered);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDU queued", fw_stats->msdu_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDU queued", fw_stats->mpdu_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDUs dropped", fw_stats->wmm_drop);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Local enqued", fw_stats->local_enqued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Local freed", fw_stats->local_freed);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HW queued", fw_stats->hw_queued);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PPDUs reaped", fw_stats->hw_reaped);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Num underruns", fw_stats->underrun);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PPDUs cleaned", fw_stats->tx_abort);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDUs requed", fw_stats->mpdus_requed);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Excessive retries", fw_stats->tx_ko);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "HW rate", fw_stats->data_rc);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Sched self tiggers", fw_stats->self_triggers);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Dropped due to SW retries",
+ fw_stats->sw_retry_failure);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Illegal rate phy errors",
+ fw_stats->illgl_rate_phy_err);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "TX timeout", fw_stats->pdev_tx_timeout);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PDEV resets", fw_stats->pdev_resets);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PHY underrun", fw_stats->phy_underrun);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDU is more than txop limit", fw_stats->txop_ovf);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath10k PDEV RX stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Mid PPDU route change",
+ fw_stats->mid_ppdu_route_change);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Tot. number of statuses", fw_stats->status_rcvd);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 0", fw_stats->r0_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 1", fw_stats->r1_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 2", fw_stats->r2_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Extra frags on rings 3", fw_stats->r3_frags);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDUs delivered to HTT", fw_stats->htt_msdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MSDUs delivered to stack", fw_stats->loc_msdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDUs delivered to stack", fw_stats->loc_mpdus);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "Oversized AMSUs", fw_stats->oversize_amsdu);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PHY errors", fw_stats->phy_errs);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "PHY errors drops", fw_stats->phy_err_drop);
+ len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
+ "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n",
+ "ath10k PEER stats");
+ len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
+ "=================");
+
+ for (i = 0; i < fw_stats->peers; i++) {
+ len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
+ "Peer MAC address",
+ fw_stats->peer_stat[i].peer_macaddr);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+ len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
+ "Peer TX rate",
+ fw_stats->peer_stat[i].peer_tx_rate);
+ len += scnprintf(buf + len, buf_len - len, "\n");
+ }
+
+ if (len > buf_len)
+ len = buf_len;
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_fw_stats = {
+ .read = ath10k_read_fw_stats,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+int ath10k_debug_create(struct ath10k *ar)
+{
+ ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
+ ar->hw->wiphy->debugfsdir);
+
+ if (!ar->debug.debugfs_phy)
+ return -ENOMEM;
+
+ init_completion(&ar->debug.event_stats_compl);
+
+ debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
+ &fops_fw_stats);
+
+ debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
+ &fops_wmi_services);
+
+ return 0;
+}
+#endif /* CONFIG_ATH10K_DEBUGFS */
+
+#ifdef CONFIG_ATH10K_DEBUG
+void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (ath10k_debug_mask & mask)
+ ath10k_printk(KERN_DEBUG, "%pV", &vaf);
+
+ trace_ath10k_log_dbg(mask, &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL(ath10k_dbg);
+
+void ath10k_dbg_dump(enum ath10k_debug_mask mask,
+ const char *msg, const char *prefix,
+ const void *buf, size_t len)
+{
+ if (ath10k_debug_mask & mask) {
+ if (msg)
+ ath10k_dbg(mask, "%s\n", msg);
+
+ print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+ }
+
+ /* tracing code doesn't like null strings :/ */
+ trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "",
+ buf, len);
+}
+EXPORT_SYMBOL(ath10k_dbg_dump);
+
+#endif /* CONFIG_ATH10K_DEBUG */