staging: wfx: add debug files and trace debug events

Add traces when debug events happen and allow to ask internal
information to chip.

These features work independently from mac80211.

Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/20190919142527.31797-16-Jerome.Pouiller@silabs.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Jérôme Pouiller 2019-09-19 14:25:44 +00:00 committed by Greg Kroah-Hartman
parent c7ff39dd8b
commit f4a71ba875
4 changed files with 203 additions and 0 deletions

View file

@ -6,11 +6,13 @@
* Copyright (c) 2010, ST-Ericsson
*/
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/crc32.h>
#include "debug.h"
#include "wfx.h"
#include "main.h"
#include "hif_tx_mib.h"
#define CREATE_TRACE_POINTS
#include "traces.h"
@ -55,6 +57,107 @@ const char *get_reg_name(unsigned long id)
return get_symbol(id, wfx_reg_print_map);
}
static int wfx_counters_show(struct seq_file *seq, void *v)
{
int ret;
struct wfx_dev *wdev = seq->private;
struct hif_mib_extended_count_table counters;
ret = hif_get_counters_table(wdev, &counters);
if (ret < 0)
return ret;
if (ret > 0)
return -EIO;
#define PUT_COUNTER(name) \
seq_printf(seq, "%24s %d\n", #name ":", le32_to_cpu(counters.count_##name))
PUT_COUNTER(tx_packets);
PUT_COUNTER(tx_multicast_frames);
PUT_COUNTER(tx_frames_success);
PUT_COUNTER(tx_frame_failures);
PUT_COUNTER(tx_frames_retried);
PUT_COUNTER(tx_frames_multi_retried);
PUT_COUNTER(rts_success);
PUT_COUNTER(rts_failures);
PUT_COUNTER(ack_failures);
PUT_COUNTER(rx_packets);
PUT_COUNTER(rx_frames_success);
PUT_COUNTER(rx_packet_errors);
PUT_COUNTER(plcp_errors);
PUT_COUNTER(fcs_errors);
PUT_COUNTER(rx_decryption_failures);
PUT_COUNTER(rx_mic_failures);
PUT_COUNTER(rx_no_key_failures);
PUT_COUNTER(rx_frame_duplicates);
PUT_COUNTER(rx_multicast_frames);
PUT_COUNTER(rx_cmacicv_errors);
PUT_COUNTER(rx_cmac_replays);
PUT_COUNTER(rx_mgmt_ccmp_replays);
PUT_COUNTER(rx_beacon);
PUT_COUNTER(miss_beacon);
#undef PUT_COUNTER
return 0;
}
DEFINE_SHOW_ATTRIBUTE(wfx_counters);
static const char * const channel_names[] = {
[0] = "1M",
[1] = "2M",
[2] = "5.5M",
[3] = "11M",
/* Entries 4 and 5 does not exist */
[6] = "6M",
[7] = "9M",
[8] = "12M",
[9] = "18M",
[10] = "24M",
[11] = "36M",
[12] = "48M",
[13] = "54M",
[14] = "MCS0",
[15] = "MCS1",
[16] = "MCS2",
[17] = "MCS3",
[18] = "MCS4",
[19] = "MCS5",
[20] = "MCS6",
[21] = "MCS7",
};
static int wfx_rx_stats_show(struct seq_file *seq, void *v)
{
struct wfx_dev *wdev = seq->private;
struct hif_rx_stats *st = &wdev->rx_stats;
int i;
mutex_lock(&wdev->rx_stats_lock);
seq_printf(seq, "Timestamp: %dus\n", st->date);
seq_printf(seq, "Low power clock: frequency %uHz, external %s\n",
st->pwr_clk_freq,
st->is_ext_pwr_clk ? "yes" : "no");
seq_printf(seq, "Num. of frames: %d, PER (x10e4): %d, Throughput: %dKbps/s\n",
st->nb_rx_frame, st->per_total, st->throughput);
seq_puts(seq, " Num. of PER RSSI SNR CFO\n");
seq_puts(seq, " frames (x10e4) (dBm) (dB) (kHz)\n");
for (i = 0; i < ARRAY_SIZE(channel_names); i++) {
if (channel_names[i])
seq_printf(seq, "%5s %8d %8d %8d %8d %8d\n",
channel_names[i], st->nb_rx_by_rate[i],
st->per[i], st->rssi[i] / 100,
st->snr[i] / 100, st->cfo[i]);
}
mutex_unlock(&wdev->rx_stats_lock);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(wfx_rx_stats);
static ssize_t wfx_send_pds_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
@ -190,6 +293,8 @@ int wfx_debug_init(struct wfx_dev *wdev)
struct dentry *d;
d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
debugfs_create_file("counters", 0444, d, wdev, &wfx_counters_fops);
debugfs_create_file("rx_stats", 0444, d, wdev, &wfx_rx_stats_fops);
debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops);
debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops);
debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops);

View file

@ -94,13 +94,93 @@ static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *
return 0;
}
static int hif_join_complete_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct wfx_vif *wvif = wdev_to_wvif(wdev, hif->interface);
WARN_ON(!wvif);
dev_warn(wdev->dev, "unattended JoinCompleteInd\n");
return 0;
}
static int hif_error_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct hif_ind_error *body = buf;
u8 *pRollback = (u8 *) body->data;
u32 *pStatus = (u32 *) body->data;
switch (body->type) {
case HIF_ERROR_FIRMWARE_ROLLBACK:
dev_err(wdev->dev, "asynchronous error: firmware rollback error %d\n", *pRollback);
break;
case HIF_ERROR_FIRMWARE_DEBUG_ENABLED:
dev_err(wdev->dev, "asynchronous error: firmware debug feature enabled\n");
break;
case HIF_ERROR_OUTDATED_SESSION_KEY:
dev_err(wdev->dev, "asynchronous error: secure link outdated key: %#.8x\n", *pStatus);
break;
case HIF_ERROR_INVALID_SESSION_KEY:
dev_err(wdev->dev, "asynchronous error: invalid session key\n");
break;
case HIF_ERROR_OOR_VOLTAGE:
dev_err(wdev->dev, "asynchronous error: out-of-range overvoltage: %#.8x\n", *pStatus);
break;
case HIF_ERROR_PDS_VERSION:
dev_err(wdev->dev, "asynchronous error: wrong PDS payload or version: %#.8x\n", *pStatus);
break;
default:
dev_err(wdev->dev, "asynchronous error: unknown (%d)\n", body->type);
break;
}
return 0;
}
static int hif_generic_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct hif_ind_generic *body = buf;
switch (body->indication_type) {
case HIF_GENERIC_INDICATION_TYPE_RAW:
return 0;
case HIF_GENERIC_INDICATION_TYPE_STRING:
dev_info(wdev->dev, "firmware says: %s\n", (char *) body->indication_data.raw_data);
return 0;
case HIF_GENERIC_INDICATION_TYPE_RX_STATS:
mutex_lock(&wdev->rx_stats_lock);
// Older firmware send a generic indication beside RxStats
if (!wfx_api_older_than(wdev, 1, 4))
dev_info(wdev->dev, "Rx test ongoing. Temperature: %d°C\n", body->indication_data.rx_stats.current_temp);
memcpy(&wdev->rx_stats, &body->indication_data.rx_stats, sizeof(wdev->rx_stats));
mutex_unlock(&wdev->rx_stats_lock);
return 0;
default:
dev_err(wdev->dev, "generic_indication: unknown indication type: %#.8x\n", body->indication_type);
return -EIO;
}
}
static int hif_exception_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
size_t len = hif->len - 4; // drop header
dev_err(wdev->dev, "firmware exception\n");
print_hex_dump_bytes("Dump: ", DUMP_PREFIX_NONE, buf, len);
wdev->chip_frozen = 1;
return -1;
}
static const struct {
int msg_id;
int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf);
} hif_handlers[] = {
{ HIF_IND_ID_STARTUP, hif_startup_indication },
{ HIF_IND_ID_WAKEUP, hif_wakeup_indication },
{ HIF_IND_ID_JOIN_COMPLETE, hif_join_complete_indication },
{ HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
{ HIF_IND_ID_GENERIC, hif_generic_indication },
{ HIF_IND_ID_ERROR, hif_error_indication },
{ HIF_IND_ID_EXCEPTION, hif_exception_indication },
};
void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)

View file

@ -212,6 +212,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup");
wfx_fill_sl_key(dev, &wdev->pdata);
mutex_init(&wdev->rx_stats_lock);
init_completion(&wdev->firmware_ready);
wfx_init_hif_cmd(&wdev->hif_cmd);
@ -220,6 +221,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
void wfx_free_common(struct wfx_dev *wdev)
{
mutex_destroy(&wdev->rx_stats_lock);
ieee80211_free_hw(wdev->hw);
}

View file

@ -38,6 +38,9 @@ struct wfx_dev {
int chip_frozen;
struct wfx_hif_cmd hif_cmd;
struct hif_rx_stats rx_stats;
struct mutex rx_stats_lock;
};
struct wfx_vif {
@ -46,4 +49,17 @@ struct wfx_vif {
int id;
};
static inline struct wfx_vif *wdev_to_wvif(struct wfx_dev *wdev, int vif_id)
{
if (vif_id >= ARRAY_SIZE(wdev->vif)) {
dev_dbg(wdev->dev, "requesting non-existent vif: %d\n", vif_id);
return NULL;
}
if (!wdev->vif[vif_id]) {
dev_dbg(wdev->dev, "requesting non-allocated vif: %d\n", vif_id);
return NULL;
}
return (struct wfx_vif *) wdev->vif[vif_id]->drv_priv;
}
#endif /* WFX_H */