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 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 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 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 */ 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 */ 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 */ 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 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 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 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 */ 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 */ 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 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 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 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 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 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 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 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 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