1 /*
2  * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "wmi_filtered_logging.h"
20 
wmi_log_buf_allocate(void)21 static struct wmi_log_buf_t *wmi_log_buf_allocate(void)
22 {
23 	struct wmi_log_buf_t *cmd_log_buf;
24 	int buf_size = WMI_FILTERED_CMD_EVT_MAX_NUM_ENTRY *
25 			sizeof(struct wmi_command_debug);
26 
27 	cmd_log_buf = qdf_mem_malloc(sizeof(struct wmi_log_buf_t));
28 	if (!cmd_log_buf)
29 		return NULL;
30 
31 	cmd_log_buf->buf = qdf_mem_malloc(buf_size);
32 	if (!cmd_log_buf->buf) {
33 		qdf_mem_free(cmd_log_buf);
34 		return NULL;
35 	}
36 	cmd_log_buf->length = 0;
37 	cmd_log_buf->buf_tail_idx = 0;
38 	cmd_log_buf->size = WMI_FILTERED_CMD_EVT_MAX_NUM_ENTRY;
39 	cmd_log_buf->p_buf_tail_idx = &cmd_log_buf->buf_tail_idx;
40 
41 	return cmd_log_buf;
42 }
43 
wmi_filtered_logging_init(wmi_unified_t wmi_handle)44 void wmi_filtered_logging_init(wmi_unified_t wmi_handle)
45 {
46 	int buf_size = WMI_FILTERED_CMD_EVT_SUPPORTED * sizeof(int);
47 
48 	/* alloc buffer to save user inputs, for WMI_CMD */
49 	wmi_handle->log_info.filtered_wmi_cmds =
50 					qdf_mem_malloc(buf_size);
51 	if (!wmi_handle->log_info.filtered_wmi_cmds)
52 		return;
53 
54 	wmi_handle->log_info.filtered_wmi_cmds_idx = 0;
55 
56 	/* alloc buffer to save user interested WMI commands */
57 	wmi_handle->log_info.wmi_filtered_command_log = wmi_log_buf_allocate();
58 	if (!wmi_handle->log_info.wmi_filtered_command_log)
59 		goto fail1;
60 
61 	/* alloc buffer to save user inputs, for WMI_EVT */
62 	wmi_handle->log_info.filtered_wmi_evts =
63 					qdf_mem_malloc(buf_size);
64 	if (!wmi_handle->log_info.filtered_wmi_evts)
65 		goto fail2;
66 
67 	wmi_handle->log_info.filtered_wmi_evts_idx = 0;
68 
69 	/* alloc buffer to save user interested WMI events */
70 	wmi_handle->log_info.wmi_filtered_event_log = wmi_log_buf_allocate();
71 	if (!wmi_handle->log_info.wmi_filtered_event_log)
72 		goto fail3;
73 
74 	return;
75 
76 fail3:
77 	qdf_mem_free(wmi_handle->log_info.filtered_wmi_evts);
78 	wmi_handle->log_info.filtered_wmi_evts = NULL;
79 fail2:
80 	qdf_mem_free(wmi_handle->log_info.wmi_filtered_command_log);
81 	wmi_handle->log_info.wmi_filtered_command_log = NULL;
82 fail1:
83 	qdf_mem_free(wmi_handle->log_info.filtered_wmi_cmds);
84 	wmi_handle->log_info.filtered_wmi_cmds = NULL;
85 }
86 
wmi_filtered_logging_free(wmi_unified_t wmi_handle)87 void wmi_filtered_logging_free(wmi_unified_t wmi_handle)
88 {
89 	if (!wmi_handle)
90 		return;
91 
92 	qdf_mem_free(wmi_handle->log_info.filtered_wmi_cmds);
93 	wmi_handle->log_info.filtered_wmi_cmds = NULL;
94 	qdf_mem_free(wmi_handle->log_info.filtered_wmi_evts);
95 	wmi_handle->log_info.filtered_wmi_evts = NULL;
96 
97 	if (wmi_handle->log_info.wmi_filtered_command_log) {
98 		qdf_mem_free(wmi_handle->log_info.
99 			     wmi_filtered_command_log->buf);
100 		wmi_handle->log_info.wmi_filtered_command_log->buf = NULL;
101 		qdf_mem_free(wmi_handle->log_info.wmi_filtered_command_log);
102 		wmi_handle->log_info.wmi_filtered_command_log = NULL;
103 	}
104 	if (wmi_handle->log_info.wmi_filtered_event_log) {
105 		qdf_mem_free(wmi_handle->log_info.
106 			     wmi_filtered_event_log->buf);
107 		wmi_handle->log_info.wmi_filtered_event_log->buf = NULL;
108 		qdf_mem_free(wmi_handle->log_info.wmi_filtered_event_log);
109 		wmi_handle->log_info.wmi_filtered_event_log = NULL;
110 	}
111 }
112 
113 /*
114  * Reset the buffer which saves user interested cmds/evts
115  */
wmi_reset_filtered_buffers(wmi_unified_t wmi_handle,struct wmi_log_buf_t * cmd_log_buf)116 static int wmi_reset_filtered_buffers(wmi_unified_t wmi_handle,
117 				      struct wmi_log_buf_t *cmd_log_buf)
118 {
119 	int buf_size = WMI_FILTERED_CMD_EVT_MAX_NUM_ENTRY *
120 			sizeof(struct wmi_command_debug);
121 
122 	if (!cmd_log_buf)
123 		return 0;
124 
125 	cmd_log_buf->length = 0;
126 	cmd_log_buf->buf_tail_idx = 0;
127 	cmd_log_buf->size = WMI_FILTERED_CMD_EVT_MAX_NUM_ENTRY;
128 	cmd_log_buf->p_buf_tail_idx = &cmd_log_buf->buf_tail_idx;
129 	qdf_mem_zero(cmd_log_buf->buf, buf_size);
130 	return 0;
131 }
132 
133 /*
134  * Check if id is in id list,
135  * return true if found.
136  */
wmi_id_in_list(uint32_t * id_list,uint32_t id)137 static bool wmi_id_in_list(uint32_t *id_list, uint32_t id)
138 {
139 	int i;
140 
141 	if (!id_list)
142 		return false;
143 
144 	for (i = 0; i < WMI_FILTERED_CMD_EVT_SUPPORTED; i++) {
145 		if (id == id_list[i]) {
146 			/* id already in target list */
147 			return true;
148 		}
149 	}
150 	return false;
151 }
152 
153 /*
154  * Add command or event ids to list to be recorded
155  */
wmi_add_to_record_list(wmi_unified_t wmi_handle,uint32_t id,enum WMI_RECORD_TYPE record_type)156 static int wmi_add_to_record_list(wmi_unified_t wmi_handle,
157 				  uint32_t id,
158 				  enum WMI_RECORD_TYPE record_type)
159 {
160 	uint32_t *target_list;
161 
162 	if (record_type == WMI_CMD) {
163 		target_list = wmi_handle->log_info.filtered_wmi_cmds;
164 		/* check if id already in target list */
165 		if (wmi_id_in_list(target_list, id))
166 			return 0;
167 		if (wmi_handle->log_info.filtered_wmi_cmds_idx >=
168 		    WMI_FILTERED_CMD_EVT_SUPPORTED) {
169 			wmi_handle->log_info.filtered_wmi_cmds_idx = 0;
170 		}
171 		target_list[wmi_handle->log_info.filtered_wmi_cmds_idx] = id;
172 		wmi_handle->log_info.filtered_wmi_cmds_idx++;
173 	} else if (record_type == WMI_EVT) {
174 		target_list = wmi_handle->log_info.filtered_wmi_evts;
175 		/* check if id already in target list */
176 		if (wmi_id_in_list(target_list, id))
177 			return 0;
178 		if (wmi_handle->log_info.filtered_wmi_evts_idx >=
179 		    WMI_FILTERED_CMD_EVT_SUPPORTED) {
180 			wmi_handle->log_info.filtered_wmi_evts_idx = 0;
181 		}
182 		target_list[wmi_handle->log_info.filtered_wmi_evts_idx] = id;
183 		wmi_handle->log_info.filtered_wmi_evts_idx++;
184 	} else {
185 		return -EINVAL;
186 	}
187 	return 0;
188 }
189 
wmi_specific_cmd_evt_record(uint32_t id,uint8_t * buf,struct wmi_log_buf_t * log_buffer)190 static void wmi_specific_cmd_evt_record(uint32_t id, uint8_t *buf,
191 					struct wmi_log_buf_t *log_buffer)
192 {
193 	int idx;
194 	struct wmi_command_debug *tmpbuf =
195 		(struct wmi_command_debug *)log_buffer->buf;
196 
197 	if (*log_buffer->p_buf_tail_idx >= WMI_FILTERED_CMD_EVT_MAX_NUM_ENTRY)
198 		*log_buffer->p_buf_tail_idx = 0;
199 
200 	idx = *log_buffer->p_buf_tail_idx;
201 	tmpbuf[idx].command = id;
202 	qdf_mem_copy(tmpbuf[idx].data, buf,
203 		     WMI_DEBUG_ENTRY_MAX_LENGTH);
204 	tmpbuf[idx].time = qdf_get_log_timestamp();
205 	(*log_buffer->p_buf_tail_idx)++;
206 	log_buffer->length++;
207 }
208 
wmi_specific_cmd_record(wmi_unified_t wmi_handle,uint32_t id,uint8_t * buf)209 void wmi_specific_cmd_record(wmi_unified_t wmi_handle,
210 			     uint32_t id, uint8_t *buf)
211 {
212 	uint32_t *target_list;
213 	struct wmi_log_buf_t *log_buffer;
214 
215 	target_list = wmi_handle->log_info.filtered_wmi_cmds;
216 	if (!target_list)
217 		return;
218 
219 	log_buffer = wmi_handle->log_info.wmi_filtered_command_log;
220 	if (!log_buffer)
221 		return;
222 
223 	if (wmi_id_in_list(target_list, id)) {
224 		/* id in target list, need to be recorded */
225 		wmi_specific_cmd_evt_record(id, buf, log_buffer);
226 	}
227 }
228 
wmi_specific_evt_record(wmi_unified_t wmi_handle,uint32_t id,uint8_t * buf)229 void wmi_specific_evt_record(wmi_unified_t wmi_handle,
230 			     uint32_t id, uint8_t *buf)
231 {
232 	uint32_t *target_list;
233 	struct wmi_log_buf_t *log_buffer;
234 
235 	target_list = wmi_handle->log_info.filtered_wmi_evts;
236 	if (!target_list)
237 		return;
238 
239 	log_buffer = wmi_handle->log_info.wmi_filtered_event_log;
240 	if (!log_buffer)
241 		return;
242 
243 	if (wmi_id_in_list(target_list, id)) {
244 		/* id in target list, need to be recorded */
245 		wmi_specific_cmd_evt_record(id, buf, log_buffer);
246 	}
247 }
248 
249 /*
250  * Debugfs read/write functions
251  */
wmi_filtered_seq_printf(qdf_debugfs_file_t m,const char * f,...)252 static int wmi_filtered_seq_printf(qdf_debugfs_file_t m, const char *f, ...)
253 {
254 	va_list args;
255 
256 	va_start(args, f);
257 	seq_vprintf(m, f, args);
258 	va_end(args);
259 
260 	return 0;
261 }
262 
263 /*
264  * debugfs show/read for filtered_wmi_cmds
265  */
debug_filtered_wmi_cmds_show(qdf_debugfs_file_t m,void * v)266 int debug_filtered_wmi_cmds_show(qdf_debugfs_file_t m, void *v)
267 {
268 	wmi_unified_t wmi_handle = (wmi_unified_t)m->private;
269 	int i;
270 	int *target_list;
271 
272 	target_list = wmi_handle->log_info.filtered_wmi_cmds;
273 	if (!target_list)
274 		return 0;
275 
276 	for (i = 0; i < WMI_FILTERED_CMD_EVT_SUPPORTED; i++) {
277 		if (target_list[i] != 0) {
278 			wmi_filtered_seq_printf(m, "0x%x ",
279 						target_list[i]);
280 		}
281 	}
282 	wmi_filtered_seq_printf(m, "\n");
283 
284 	return 0;
285 }
286 
debug_filtered_wmi_evts_show(qdf_debugfs_file_t m,void * v)287 int debug_filtered_wmi_evts_show(qdf_debugfs_file_t m, void *v)
288 {
289 	wmi_unified_t wmi_handle = (wmi_unified_t)m->private;
290 	int i;
291 	int *target_list;
292 
293 	target_list = wmi_handle->log_info.filtered_wmi_evts;
294 	if (!target_list)
295 		return 0;
296 	for (i = 0; i < WMI_FILTERED_CMD_EVT_SUPPORTED; i++) {
297 		if (target_list[i] != 0) {
298 			wmi_filtered_seq_printf(m, "0x%x ",
299 						target_list[i]);
300 		}
301 	}
302 	wmi_filtered_seq_printf(m, "\n");
303 
304 	return 0;
305 }
306 
wmi_log_show(wmi_unified_t wmi_handle,void * buf,qdf_debugfs_file_t m)307 static int wmi_log_show(wmi_unified_t wmi_handle, void *buf,
308 			qdf_debugfs_file_t m)
309 {
310 	struct wmi_log_buf_t *wmi_log = (struct wmi_log_buf_t *)buf;
311 	int pos, nread, outlen;
312 	int i;
313 	uint64_t secs, usecs;
314 	int wmi_ring_size = 100;
315 
316 	qdf_spin_lock_bh(&wmi_handle->log_info.wmi_record_lock);
317 	if (!wmi_log->length) {
318 		qdf_spin_unlock_bh(&wmi_handle->log_info.wmi_record_lock);
319 		return wmi_filtered_seq_printf(m,
320 					       "Nothing to read!\n");
321 	}
322 	if (wmi_log->length <= wmi_ring_size)
323 		nread = wmi_log->length;
324 	else
325 		nread = wmi_ring_size;
326 
327 	if (*wmi_log->p_buf_tail_idx == 0)
328 		/* tail can be 0 after wrap-around */
329 		pos = wmi_ring_size - 1;
330 	else
331 		pos = *wmi_log->p_buf_tail_idx - 1;
332 
333 	outlen = wmi_filtered_seq_printf(m, "Length = %d\n", wmi_log->length);
334 	qdf_spin_unlock_bh(&wmi_handle->log_info.wmi_record_lock);
335 	while (nread--) {
336 		struct wmi_event_debug *wmi_record;
337 
338 		wmi_record = &(((struct wmi_event_debug *)wmi_log->buf)[pos]);
339 		qdf_log_timestamp_to_secs(wmi_record->time, &secs,
340 					  &usecs);
341 		outlen += wmi_filtered_seq_printf(m, "Event ID = %x\n",
342 						  (wmi_record->event));
343 		outlen +=
344 			wmi_filtered_seq_printf(m,
345 						"Event TIME = [%llu.%06llu]\n",
346 						secs, usecs);
347 		outlen += wmi_filtered_seq_printf(m, "CMD = ");
348 		for (i = 0; i < (WMI_DEBUG_ENTRY_MAX_LENGTH /
349 				sizeof(uint32_t)); i++)
350 			outlen += wmi_filtered_seq_printf(m, "%x ",
351 							  wmi_record->data[i]);
352 		outlen += wmi_filtered_seq_printf(m, "\n");
353 		if (pos == 0)
354 			pos = wmi_ring_size - 1;
355 		else
356 			pos--;
357 	}
358 	return outlen;
359 }
360 
debug_wmi_filtered_command_log_show(qdf_debugfs_file_t m,void * v)361 int debug_wmi_filtered_command_log_show(qdf_debugfs_file_t m, void *v)
362 {
363 	wmi_unified_t wmi_handle = (wmi_unified_t)m->private;
364 	struct wmi_log_buf_t *wmi_log =
365 		wmi_handle->log_info.wmi_filtered_command_log;
366 
367 	if (!wmi_log)
368 		return 0;
369 	return wmi_log_show(wmi_handle, wmi_log, m);
370 }
371 
debug_wmi_filtered_event_log_show(qdf_debugfs_file_t m,void * v)372 int debug_wmi_filtered_event_log_show(qdf_debugfs_file_t m, void *v)
373 {
374 	wmi_unified_t wmi_handle = (wmi_unified_t)m->private;
375 	struct wmi_log_buf_t *wmi_log =
376 		wmi_handle->log_info.wmi_filtered_event_log;
377 
378 	if (!wmi_log)
379 		return 0;
380 	return wmi_log_show(wmi_handle, wmi_log, m);
381 }
382 
debug_filtered_wmi_cmds_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)383 ssize_t debug_filtered_wmi_cmds_write(struct file *file,
384 				      const char __user *buf,
385 				      size_t count, loff_t *ppos)
386 {
387 	wmi_unified_t wmi_handle =
388 		((struct seq_file *)file->private_data)->private;
389 	int k, ret;
390 	char locbuf[12] = {0};
391 	int buf_size = WMI_FILTERED_CMD_EVT_SUPPORTED * sizeof(int);
392 
393 	if ((!buf) || (count > 8 || count <= 0))
394 		return -EFAULT;
395 
396 	if (!wmi_handle->log_info.filtered_wmi_cmds)
397 		return -EFAULT;
398 
399 	if (copy_from_user(locbuf, buf, count))
400 		return -EFAULT;
401 
402 	ret = qdf_kstrtoint(locbuf, 16, &k);
403 	if (ret)
404 		return -EINVAL;
405 
406 	if (k == 0xffff) {
407 		qdf_mem_zero(wmi_handle->log_info.filtered_wmi_cmds, buf_size);
408 		wmi_handle->log_info.filtered_wmi_cmds_idx = 0;
409 		return count;
410 	}
411 
412 	if (wmi_add_to_record_list(wmi_handle, k, WMI_CMD)) {
413 		wmi_err("Add cmd %d to WMI_CMD list failed", k);
414 		return 0;
415 	}
416 
417 	return count;
418 }
419 
debug_filtered_wmi_evts_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)420 ssize_t debug_filtered_wmi_evts_write(struct file *file,
421 				      const char __user *buf,
422 				      size_t count, loff_t *ppos)
423 {
424 	wmi_unified_t wmi_handle =
425 		((struct seq_file *)file->private_data)->private;
426 	int k, ret;
427 	char locbuf[12] = {0};
428 	int buf_size = WMI_FILTERED_CMD_EVT_SUPPORTED * sizeof(int);
429 
430 	if ((!buf) || (count > 8 || count <= 0))
431 		return -EFAULT;
432 
433 	if (!wmi_handle->log_info.filtered_wmi_evts)
434 		return -EFAULT;
435 
436 	if (copy_from_user(locbuf, buf, count))
437 		return -EFAULT;
438 
439 	ret = qdf_kstrtoint(locbuf, 16, &k);
440 	if (ret)
441 		return -EINVAL;
442 
443 	if (k == 0xffff) {
444 		qdf_mem_zero(wmi_handle->log_info.filtered_wmi_evts, buf_size);
445 		wmi_handle->log_info.filtered_wmi_evts_idx = 0;
446 		return count;
447 	}
448 
449 	if (wmi_add_to_record_list(wmi_handle, k, WMI_EVT)) {
450 		wmi_err("Add cmd %d to WMI_EVT list failed", k);
451 		return 0;
452 	}
453 
454 	return count;
455 }
456 
debug_wmi_filtered_command_log_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)457 ssize_t debug_wmi_filtered_command_log_write(struct file *file,
458 					     const char __user *buf,
459 					     size_t count, loff_t *ppos)
460 {
461 	wmi_unified_t wmi_handle =
462 		((struct seq_file *)file->private_data)->private;
463 	int k, ret;
464 	char locbuf[12] = {0};
465 	struct wmi_log_buf_t *cmd_log_buf;
466 
467 	if ((!buf) || (count > 8 || count <= 0))
468 		return -EFAULT;
469 
470 	if (copy_from_user(locbuf, buf, count))
471 		return -EFAULT;
472 
473 	ret = qdf_kstrtoint(locbuf, 16, &k);
474 	if (ret)
475 		return -EINVAL;
476 
477 	if (k != 0xffff)
478 		return -EINVAL;
479 
480 	cmd_log_buf = wmi_handle->log_info.wmi_filtered_command_log;
481 	if (wmi_reset_filtered_buffers(wmi_handle, cmd_log_buf))
482 		wmi_err("reset WMI CMD filtered_buffers failed");
483 	return count;
484 }
485 
debug_wmi_filtered_event_log_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)486 ssize_t debug_wmi_filtered_event_log_write(struct file *file,
487 					   const char __user *buf,
488 					   size_t count, loff_t *ppos)
489 {
490 	wmi_unified_t wmi_handle =
491 		((struct seq_file *)file->private_data)->private;
492 	int k, ret;
493 	char locbuf[12] = {0};
494 	struct wmi_log_buf_t *cmd_log_buf;
495 
496 	if ((!buf) || (count > 8 || count <= 0))
497 		return -EFAULT;
498 
499 	if (copy_from_user(locbuf, buf, count))
500 		return -EFAULT;
501 
502 	ret = qdf_kstrtoint(locbuf, 16, &k);
503 	if (ret)
504 		return -EINVAL;
505 
506 	if (k != 0xffff)
507 		return -EINVAL;
508 
509 	cmd_log_buf = wmi_handle->log_info.wmi_filtered_event_log;
510 	if (wmi_reset_filtered_buffers(wmi_handle, cmd_log_buf))
511 		wmi_err("reset WMI EVT filtered_buffers failed");
512 	return count;
513 }
514