at v5.7-rc5 311 lines 7.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Debugfs interface. 4 * 5 * Copyright (c) 2017-2019, Silicon Laboratories, Inc. 6 * Copyright (c) 2010, ST-Ericsson 7 */ 8#include <linux/debugfs.h> 9#include <linux/seq_file.h> 10#include <linux/crc32.h> 11 12#include "debug.h" 13#include "wfx.h" 14#include "sta.h" 15#include "main.h" 16#include "hif_tx.h" 17#include "hif_tx_mib.h" 18 19#define CREATE_TRACE_POINTS 20#include "traces.h" 21 22static const struct trace_print_flags hif_msg_print_map[] = { 23 hif_msg_list, 24}; 25 26static const struct trace_print_flags hif_mib_print_map[] = { 27 hif_mib_list, 28}; 29 30static const struct trace_print_flags wfx_reg_print_map[] = { 31 wfx_reg_list, 32}; 33 34static const char *get_symbol(unsigned long val, 35 const struct trace_print_flags *symbol_array) 36{ 37 int i; 38 39 for (i = 0; symbol_array[i].mask != -1; i++) { 40 if (val == symbol_array[i].mask) 41 return symbol_array[i].name; 42 } 43 44 return "unknown"; 45} 46 47const char *get_hif_name(unsigned long id) 48{ 49 return get_symbol(id, hif_msg_print_map); 50} 51 52const char *get_mib_name(unsigned long id) 53{ 54 return get_symbol(id, hif_mib_print_map); 55} 56 57const char *get_reg_name(unsigned long id) 58{ 59 return get_symbol(id, wfx_reg_print_map); 60} 61 62static int wfx_counters_show(struct seq_file *seq, void *v) 63{ 64 int ret; 65 struct wfx_dev *wdev = seq->private; 66 struct hif_mib_extended_count_table counters; 67 68 ret = hif_get_counters_table(wdev, &counters); 69 if (ret < 0) 70 return ret; 71 if (ret > 0) 72 return -EIO; 73 74#define PUT_COUNTER(name) \ 75 seq_printf(seq, "%24s %d\n", #name ":",\ 76 le32_to_cpu(counters.count_##name)) 77 78 PUT_COUNTER(tx_packets); 79 PUT_COUNTER(tx_multicast_frames); 80 PUT_COUNTER(tx_frames_success); 81 PUT_COUNTER(tx_frame_failures); 82 PUT_COUNTER(tx_frames_retried); 83 PUT_COUNTER(tx_frames_multi_retried); 84 85 PUT_COUNTER(rts_success); 86 PUT_COUNTER(rts_failures); 87 PUT_COUNTER(ack_failures); 88 89 PUT_COUNTER(rx_packets); 90 PUT_COUNTER(rx_frames_success); 91 PUT_COUNTER(rx_packet_errors); 92 PUT_COUNTER(plcp_errors); 93 PUT_COUNTER(fcs_errors); 94 PUT_COUNTER(rx_decryption_failures); 95 PUT_COUNTER(rx_mic_failures); 96 PUT_COUNTER(rx_no_key_failures); 97 PUT_COUNTER(rx_frame_duplicates); 98 PUT_COUNTER(rx_multicast_frames); 99 PUT_COUNTER(rx_cmacicv_errors); 100 PUT_COUNTER(rx_cmac_replays); 101 PUT_COUNTER(rx_mgmt_ccmp_replays); 102 103 PUT_COUNTER(rx_beacon); 104 PUT_COUNTER(miss_beacon); 105 106#undef PUT_COUNTER 107 108 return 0; 109} 110DEFINE_SHOW_ATTRIBUTE(wfx_counters); 111 112static const char * const channel_names[] = { 113 [0] = "1M", 114 [1] = "2M", 115 [2] = "5.5M", 116 [3] = "11M", 117 /* Entries 4 and 5 does not exist */ 118 [6] = "6M", 119 [7] = "9M", 120 [8] = "12M", 121 [9] = "18M", 122 [10] = "24M", 123 [11] = "36M", 124 [12] = "48M", 125 [13] = "54M", 126 [14] = "MCS0", 127 [15] = "MCS1", 128 [16] = "MCS2", 129 [17] = "MCS3", 130 [18] = "MCS4", 131 [19] = "MCS5", 132 [20] = "MCS6", 133 [21] = "MCS7", 134}; 135 136static int wfx_rx_stats_show(struct seq_file *seq, void *v) 137{ 138 struct wfx_dev *wdev = seq->private; 139 struct hif_rx_stats *st = &wdev->rx_stats; 140 int i; 141 142 mutex_lock(&wdev->rx_stats_lock); 143 seq_printf(seq, "Timestamp: %dus\n", st->date); 144 seq_printf(seq, "Low power clock: frequency %uHz, external %s\n", 145 st->pwr_clk_freq, 146 st->is_ext_pwr_clk ? "yes" : "no"); 147 seq_printf(seq, 148 "Num. of frames: %d, PER (x10e4): %d, Throughput: %dKbps/s\n", 149 st->nb_rx_frame, st->per_total, st->throughput); 150 seq_puts(seq, " Num. of PER RSSI SNR CFO\n"); 151 seq_puts(seq, " frames (x10e4) (dBm) (dB) (kHz)\n"); 152 for (i = 0; i < ARRAY_SIZE(channel_names); i++) { 153 if (channel_names[i]) 154 seq_printf(seq, "%5s %8d %8d %8d %8d %8d\n", 155 channel_names[i], st->nb_rx_by_rate[i], 156 st->per[i], st->rssi[i] / 100, 157 st->snr[i] / 100, st->cfo[i]); 158 } 159 mutex_unlock(&wdev->rx_stats_lock); 160 161 return 0; 162} 163DEFINE_SHOW_ATTRIBUTE(wfx_rx_stats); 164 165static ssize_t wfx_send_pds_write(struct file *file, 166 const char __user *user_buf, 167 size_t count, loff_t *ppos) 168{ 169 struct wfx_dev *wdev = file->private_data; 170 char *buf; 171 int ret; 172 173 if (*ppos != 0) { 174 dev_dbg(wdev->dev, "PDS data must be written in one transaction"); 175 return -EBUSY; 176 } 177 buf = memdup_user(user_buf, count); 178 if (IS_ERR(buf)) 179 return PTR_ERR(buf); 180 *ppos = *ppos + count; 181 ret = wfx_send_pds(wdev, buf, count); 182 kfree(buf); 183 if (ret < 0) 184 return ret; 185 return count; 186} 187 188static const struct file_operations wfx_send_pds_fops = { 189 .open = simple_open, 190 .write = wfx_send_pds_write, 191}; 192 193static ssize_t wfx_burn_slk_key_write(struct file *file, 194 const char __user *user_buf, 195 size_t count, loff_t *ppos) 196{ 197 struct wfx_dev *wdev = file->private_data; 198 199 dev_info(wdev->dev, "this driver does not support secure link\n"); 200 return -EINVAL; 201} 202 203static const struct file_operations wfx_burn_slk_key_fops = { 204 .open = simple_open, 205 .write = wfx_burn_slk_key_write, 206}; 207 208struct dbgfs_hif_msg { 209 struct wfx_dev *wdev; 210 struct completion complete; 211 u8 reply[1024]; 212 int ret; 213}; 214 215static ssize_t wfx_send_hif_msg_write(struct file *file, 216 const char __user *user_buf, 217 size_t count, loff_t *ppos) 218{ 219 struct dbgfs_hif_msg *context = file->private_data; 220 struct wfx_dev *wdev = context->wdev; 221 struct hif_msg *request; 222 223 if (completion_done(&context->complete)) { 224 dev_dbg(wdev->dev, "read previous result before start a new one\n"); 225 return -EBUSY; 226 } 227 if (count < sizeof(struct hif_msg)) 228 return -EINVAL; 229 230 // wfx_cmd_send() chekc that reply buffer is wide enough, but do not 231 // return precise length read. User have to know how many bytes should 232 // be read. Filling reply buffer with a memory pattern may help user. 233 memset(context->reply, 0xFF, sizeof(context->reply)); 234 request = memdup_user(user_buf, count); 235 if (IS_ERR(request)) 236 return PTR_ERR(request); 237 if (request->len != count) { 238 kfree(request); 239 return -EINVAL; 240 } 241 context->ret = wfx_cmd_send(wdev, request, context->reply, 242 sizeof(context->reply), false); 243 244 kfree(request); 245 complete(&context->complete); 246 return count; 247} 248 249static ssize_t wfx_send_hif_msg_read(struct file *file, char __user *user_buf, 250 size_t count, loff_t *ppos) 251{ 252 struct dbgfs_hif_msg *context = file->private_data; 253 int ret; 254 255 if (count > sizeof(context->reply)) 256 return -EINVAL; 257 ret = wait_for_completion_interruptible(&context->complete); 258 if (ret) 259 return ret; 260 if (context->ret < 0) 261 return context->ret; 262 // Be carefull, write() is waiting for a full message while read() 263 // only return a payload 264 if (copy_to_user(user_buf, context->reply, count)) 265 return -EFAULT; 266 267 return count; 268} 269 270static int wfx_send_hif_msg_open(struct inode *inode, struct file *file) 271{ 272 struct dbgfs_hif_msg *context = kzalloc(sizeof(*context), GFP_KERNEL); 273 274 if (!context) 275 return -ENOMEM; 276 context->wdev = inode->i_private; 277 init_completion(&context->complete); 278 file->private_data = context; 279 return 0; 280} 281 282static int wfx_send_hif_msg_release(struct inode *inode, struct file *file) 283{ 284 struct dbgfs_hif_msg *context = file->private_data; 285 286 kfree(context); 287 return 0; 288} 289 290static const struct file_operations wfx_send_hif_msg_fops = { 291 .open = wfx_send_hif_msg_open, 292 .release = wfx_send_hif_msg_release, 293 .write = wfx_send_hif_msg_write, 294 .read = wfx_send_hif_msg_read, 295}; 296 297int wfx_debug_init(struct wfx_dev *wdev) 298{ 299 struct dentry *d; 300 301 d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir); 302 debugfs_create_file("counters", 0444, d, wdev, &wfx_counters_fops); 303 debugfs_create_file("rx_stats", 0444, d, wdev, &wfx_rx_stats_fops); 304 debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops); 305 debugfs_create_file("burn_slk_key", 0200, d, wdev, 306 &wfx_burn_slk_key_fops); 307 debugfs_create_file("send_hif_msg", 0600, d, wdev, 308 &wfx_send_hif_msg_fops); 309 310 return 0; 311}