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