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