1 /* 2 * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #ifndef REMOVE_PKT_LOG 21 #ifndef EXPORT_SYMTAB 22 #define EXPORT_SYMTAB 23 #endif 24 #ifndef __KERNEL__ 25 #define __KERNEL__ 26 #endif 27 /* 28 * Linux specific implementation of Pktlogs for 802.11ac 29 */ 30 #include <linux/kernel.h> 31 #include <linux/init.h> 32 #include <linux/module.h> 33 #include <linux/vmalloc.h> 34 #include <linux/proc_fs.h> 35 #include <pktlog_ac_i.h> 36 #include <pktlog_ac_fmt.h> 37 #include "i_host_diag_core_log.h" 38 #include "host_diag_core_log.h" 39 #include "ani_global.h" 40 41 #define PKTLOG_DEVNAME_SIZE 32 42 #define MAX_WLANDEV 1 43 44 #ifdef MULTI_IF_NAME 45 #define PKTLOG_PROC_DIR "ath_pktlog" MULTI_IF_NAME 46 #else 47 #define PKTLOG_PROC_DIR "ath_pktlog" 48 #endif 49 50 /* Permissions for creating proc entries */ 51 #define PKTLOG_PROC_PERM 0444 52 #define PKTLOG_PROCSYS_DIR_PERM 0555 53 #define PKTLOG_PROCSYS_PERM 0644 54 55 #ifndef __MOD_INC_USE_COUNT 56 #define PKTLOG_MOD_INC_USE_COUNT do { \ 57 if (!try_module_get(THIS_MODULE)) { \ 58 qdf_nofl_info("try_module_get failed"); \ 59 } } while (0) 60 61 #define PKTLOG_MOD_DEC_USE_COUNT module_put(THIS_MODULE) 62 #else 63 #define PKTLOG_MOD_INC_USE_COUNT MOD_INC_USE_COUNT 64 #define PKTLOG_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT 65 #endif 66 67 static struct ath_pktlog_info *g_pktlog_info; 68 69 static struct proc_dir_entry *g_pktlog_pde; 70 71 static DEFINE_MUTEX(proc_mutex); 72 73 static int pktlog_attach(struct hif_opaque_softc *scn); 74 static void pktlog_detach(struct hif_opaque_softc *scn); 75 static int pktlog_open(struct inode *i, struct file *f); 76 static int pktlog_release(struct inode *i, struct file *f); 77 static ssize_t pktlog_read(struct file *file, char *buf, size_t nbytes, 78 loff_t *ppos); 79 80 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) 81 static const struct proc_ops pktlog_fops = { 82 .proc_open = pktlog_open, 83 .proc_release = pktlog_release, 84 .proc_read = pktlog_read, 85 .proc_lseek = default_llseek, 86 }; 87 #else 88 static struct file_operations pktlog_fops = { 89 open: pktlog_open, 90 release:pktlog_release, 91 read : pktlog_read, 92 }; 93 #endif 94 95 void pktlog_disable_adapter_logging(struct hif_opaque_softc *scn) 96 { 97 struct pktlog_dev_t *pl_dev = get_pktlog_handle(); 98 if (pl_dev) 99 pl_dev->pl_info->log_state = 0; 100 } 101 102 int pktlog_alloc_buf(struct hif_opaque_softc *scn) 103 { 104 uint32_t page_cnt; 105 unsigned long vaddr; 106 struct page *vpg; 107 struct pktlog_dev_t *pl_dev; 108 struct ath_pktlog_info *pl_info; 109 struct ath_pktlog_buf *buffer; 110 111 pl_dev = get_pktlog_handle(); 112 113 if (!pl_dev) { 114 qdf_info(PKTLOG_TAG "pdev_txrx_handle->pl_dev is null"); 115 return -EINVAL; 116 } 117 118 pl_info = pl_dev->pl_info; 119 120 page_cnt = (sizeof(*(pl_info->buf)) + pl_info->buf_size) / PAGE_SIZE; 121 122 qdf_spin_lock_bh(&pl_info->log_lock); 123 if (pl_info->buf) { 124 qdf_spin_unlock_bh(&pl_info->log_lock); 125 qdf_nofl_info(PKTLOG_TAG "Buffer is already in use"); 126 return -EINVAL; 127 } 128 qdf_spin_unlock_bh(&pl_info->log_lock); 129 130 buffer = qdf_mem_valloc((page_cnt + 2) * PAGE_SIZE); 131 if (!buffer) { 132 return -ENOMEM; 133 } 134 135 buffer = (struct ath_pktlog_buf *) 136 (((unsigned long)(buffer) + PAGE_SIZE - 1) 137 & PAGE_MASK); 138 139 for (vaddr = (unsigned long)(buffer); 140 vaddr < ((unsigned long)(buffer) + (page_cnt * PAGE_SIZE)); 141 vaddr += PAGE_SIZE) { 142 vpg = vmalloc_to_page((const void *)vaddr); 143 SetPageReserved(vpg); 144 } 145 146 qdf_spin_lock_bh(&pl_info->log_lock); 147 if (pl_info->buf) 148 pktlog_release_buf(scn); 149 150 pl_info->buf = buffer; 151 qdf_spin_unlock_bh(&pl_info->log_lock); 152 return 0; 153 } 154 155 void pktlog_release_buf(struct hif_opaque_softc *scn) 156 { 157 unsigned long page_cnt; 158 unsigned long vaddr; 159 struct page *vpg; 160 struct pktlog_dev_t *pl_dev; 161 struct ath_pktlog_info *pl_info; 162 163 pl_dev = get_pktlog_handle(); 164 165 if (!pl_dev) { 166 qdf_print("Invalid pl_dev handle"); 167 return; 168 } 169 170 if (!pl_dev->pl_info) { 171 qdf_print("Invalid pl_dev handle"); 172 return; 173 } 174 175 pl_info = pl_dev->pl_info; 176 177 page_cnt = ((sizeof(*(pl_info->buf)) + pl_info->buf_size) / 178 PAGE_SIZE) + 1; 179 180 for (vaddr = (unsigned long)(pl_info->buf); 181 vaddr < (unsigned long)(pl_info->buf) + (page_cnt * PAGE_SIZE); 182 vaddr += PAGE_SIZE) { 183 vpg = vmalloc_to_page((const void *)vaddr); 184 ClearPageReserved(vpg); 185 } 186 187 qdf_mem_vfree(pl_info->buf); 188 pl_info->buf = NULL; 189 } 190 191 static void pktlog_cleanup(struct ath_pktlog_info *pl_info) 192 { 193 pl_info->log_state = 0; 194 PKTLOG_LOCK_DESTROY(pl_info); 195 mutex_destroy(&pl_info->pktlog_mutex); 196 } 197 198 /* sysctl procfs handler to enable pktlog */ 199 static int 200 qdf_sysctl_decl(ath_sysctl_pktlog_enable, ctl, write, filp, buffer, lenp, ppos) 201 { 202 int ret, enable; 203 ol_ath_generic_softc_handle scn; 204 struct pktlog_dev_t *pl_dev; 205 206 mutex_lock(&proc_mutex); 207 scn = (ol_ath_generic_softc_handle) ctl->extra1; 208 209 if (!scn) { 210 mutex_unlock(&proc_mutex); 211 qdf_info("Invalid scn context"); 212 ASSERT(0); 213 return -EINVAL; 214 } 215 216 pl_dev = get_pktlog_handle(); 217 218 if (!pl_dev) { 219 mutex_unlock(&proc_mutex); 220 qdf_info("Invalid pktlog context"); 221 ASSERT(0); 222 return -ENODEV; 223 } 224 225 ctl->data = &enable; 226 ctl->maxlen = sizeof(enable); 227 228 if (write) { 229 ret = QDF_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, 230 lenp, ppos); 231 if (ret == 0) { 232 ret = pl_dev->pl_funcs->pktlog_enable( 233 (struct hif_opaque_softc *)scn, enable, 234 cds_is_packet_log_enabled(), 0, 1); 235 } 236 else 237 QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_DEBUG, 238 "Line:%d %s:proc_dointvec failed reason %d", 239 __LINE__, __func__, ret); 240 } else { 241 ret = QDF_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, 242 lenp, ppos); 243 if (ret) 244 QDF_TRACE(QDF_MODULE_ID_SYS, QDF_TRACE_LEVEL_DEBUG, 245 "Line:%d %s:proc_dointvec failed reason %d", 246 __LINE__, __func__, ret); 247 } 248 249 ctl->data = NULL; 250 ctl->maxlen = 0; 251 mutex_unlock(&proc_mutex); 252 253 return ret; 254 } 255 256 static int get_pktlog_bufsize(struct pktlog_dev_t *pl_dev) 257 { 258 return pl_dev->pl_info->buf_size; 259 } 260 261 /* sysctl procfs handler to set/get pktlog size */ 262 static int 263 qdf_sysctl_decl(ath_sysctl_pktlog_size, ctl, write, filp, buffer, lenp, ppos) 264 { 265 int ret, size; 266 ol_ath_generic_softc_handle scn; 267 struct pktlog_dev_t *pl_dev; 268 269 mutex_lock(&proc_mutex); 270 scn = (ol_ath_generic_softc_handle) ctl->extra1; 271 272 if (!scn) { 273 mutex_unlock(&proc_mutex); 274 qdf_info("Invalid scn context"); 275 ASSERT(0); 276 return -EINVAL; 277 } 278 279 pl_dev = get_pktlog_handle(); 280 281 if (!pl_dev) { 282 mutex_unlock(&proc_mutex); 283 qdf_info("Invalid pktlog handle"); 284 ASSERT(0); 285 return -ENODEV; 286 } 287 288 ctl->data = &size; 289 ctl->maxlen = sizeof(size); 290 291 if (write) { 292 ret = QDF_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, 293 lenp, ppos); 294 if (ret == 0) 295 ret = pl_dev->pl_funcs->pktlog_setsize( 296 (struct hif_opaque_softc *)scn, size); 297 } else { 298 size = get_pktlog_bufsize(pl_dev); 299 ret = QDF_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, 300 lenp, ppos); 301 } 302 303 ctl->data = NULL; 304 ctl->maxlen = 0; 305 mutex_unlock(&proc_mutex); 306 307 return ret; 308 } 309 310 /* Register sysctl table */ 311 static int pktlog_sysctl_register(struct hif_opaque_softc *scn) 312 { 313 struct pktlog_dev_t *pl_dev = get_pktlog_handle(); 314 struct ath_pktlog_info_lnx *pl_info_lnx; 315 char *proc_name; 316 317 if (pl_dev) { 318 pl_info_lnx = PL_INFO_LNX(pl_dev->pl_info); 319 proc_name = pl_dev->name; 320 } else { 321 pl_info_lnx = PL_INFO_LNX(g_pktlog_info); 322 proc_name = PKTLOG_PROC_SYSTEM; 323 } 324 325 /* 326 * Setup the sysctl table for creating the following sysctl entries: 327 * /proc/sys/PKTLOG_PROC_DIR/<adapter>/enable for enabling/disabling 328 * pktlog 329 * /proc/sys/PKTLOG_PROC_DIR/<adapter>/size for changing the buffer size 330 */ 331 memset(pl_info_lnx->sysctls, 0, sizeof(pl_info_lnx->sysctls)); 332 pl_info_lnx->sysctls[0].procname = PKTLOG_PROC_DIR; 333 pl_info_lnx->sysctls[0].mode = PKTLOG_PROCSYS_DIR_PERM; 334 pl_info_lnx->sysctls[0].child = &pl_info_lnx->sysctls[2]; 335 336 /* [1] is NULL terminator */ 337 pl_info_lnx->sysctls[2].procname = proc_name; 338 pl_info_lnx->sysctls[2].mode = PKTLOG_PROCSYS_DIR_PERM; 339 pl_info_lnx->sysctls[2].child = &pl_info_lnx->sysctls[4]; 340 341 /* [3] is NULL terminator */ 342 pl_info_lnx->sysctls[4].procname = "enable"; 343 pl_info_lnx->sysctls[4].mode = PKTLOG_PROCSYS_PERM; 344 pl_info_lnx->sysctls[4].proc_handler = ath_sysctl_pktlog_enable; 345 pl_info_lnx->sysctls[4].extra1 = scn; 346 347 pl_info_lnx->sysctls[5].procname = "size"; 348 pl_info_lnx->sysctls[5].mode = PKTLOG_PROCSYS_PERM; 349 pl_info_lnx->sysctls[5].proc_handler = ath_sysctl_pktlog_size; 350 pl_info_lnx->sysctls[5].extra1 = scn; 351 352 pl_info_lnx->sysctls[6].procname = "options"; 353 pl_info_lnx->sysctls[6].mode = PKTLOG_PROCSYS_PERM; 354 pl_info_lnx->sysctls[6].proc_handler = proc_dointvec; 355 pl_info_lnx->sysctls[6].data = &pl_info_lnx->info.options; 356 pl_info_lnx->sysctls[6].maxlen = sizeof(pl_info_lnx->info.options); 357 358 pl_info_lnx->sysctls[7].procname = "sack_thr"; 359 pl_info_lnx->sysctls[7].mode = PKTLOG_PROCSYS_PERM; 360 pl_info_lnx->sysctls[7].proc_handler = proc_dointvec; 361 pl_info_lnx->sysctls[7].data = &pl_info_lnx->info.sack_thr; 362 pl_info_lnx->sysctls[7].maxlen = sizeof(pl_info_lnx->info.sack_thr); 363 364 pl_info_lnx->sysctls[8].procname = "tail_length"; 365 pl_info_lnx->sysctls[8].mode = PKTLOG_PROCSYS_PERM; 366 pl_info_lnx->sysctls[8].proc_handler = proc_dointvec; 367 pl_info_lnx->sysctls[8].data = &pl_info_lnx->info.tail_length; 368 pl_info_lnx->sysctls[8].maxlen = sizeof(pl_info_lnx->info.tail_length); 369 370 pl_info_lnx->sysctls[9].procname = "thruput_thresh"; 371 pl_info_lnx->sysctls[9].mode = PKTLOG_PROCSYS_PERM; 372 pl_info_lnx->sysctls[9].proc_handler = proc_dointvec; 373 pl_info_lnx->sysctls[9].data = &pl_info_lnx->info.thruput_thresh; 374 pl_info_lnx->sysctls[9].maxlen = 375 sizeof(pl_info_lnx->info.thruput_thresh); 376 377 pl_info_lnx->sysctls[10].procname = "phyerr_thresh"; 378 pl_info_lnx->sysctls[10].mode = PKTLOG_PROCSYS_PERM; 379 pl_info_lnx->sysctls[10].proc_handler = proc_dointvec; 380 pl_info_lnx->sysctls[10].data = &pl_info_lnx->info.phyerr_thresh; 381 pl_info_lnx->sysctls[10].maxlen = 382 sizeof(pl_info_lnx->info.phyerr_thresh); 383 384 pl_info_lnx->sysctls[11].procname = "per_thresh"; 385 pl_info_lnx->sysctls[11].mode = PKTLOG_PROCSYS_PERM; 386 pl_info_lnx->sysctls[11].proc_handler = proc_dointvec; 387 pl_info_lnx->sysctls[11].data = &pl_info_lnx->info.per_thresh; 388 pl_info_lnx->sysctls[11].maxlen = sizeof(pl_info_lnx->info.per_thresh); 389 390 pl_info_lnx->sysctls[12].procname = "trigger_interval"; 391 pl_info_lnx->sysctls[12].mode = PKTLOG_PROCSYS_PERM; 392 pl_info_lnx->sysctls[12].proc_handler = proc_dointvec; 393 pl_info_lnx->sysctls[12].data = &pl_info_lnx->info.trigger_interval; 394 pl_info_lnx->sysctls[12].maxlen = 395 sizeof(pl_info_lnx->info.trigger_interval); 396 /* [13] is NULL terminator */ 397 398 /* and register everything */ 399 /* register_sysctl_table changed from 2.6.21 onwards */ 400 pl_info_lnx->sysctl_header = 401 register_sysctl_table(pl_info_lnx->sysctls); 402 403 if (!pl_info_lnx->sysctl_header) { 404 qdf_nofl_info("%s: failed to register sysctls!", proc_name); 405 return -EINVAL; 406 } 407 408 return 0; 409 } 410 411 /* 412 * Initialize logging for system or adapter 413 * Parameter scn should be NULL for system wide logging 414 */ 415 static int pktlog_attach(struct hif_opaque_softc *scn) 416 { 417 struct pktlog_dev_t *pl_dev; 418 struct ath_pktlog_info_lnx *pl_info_lnx; 419 char *proc_name; 420 struct proc_dir_entry *proc_entry; 421 422 qdf_info("attach pktlog resources"); 423 /* Allocate pktlog dev for later use */ 424 pl_dev = get_pktlog_handle(); 425 426 if (pl_dev) { 427 pl_info_lnx = kmalloc(sizeof(*pl_info_lnx), GFP_KERNEL); 428 if (!pl_info_lnx) { 429 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 430 "%s: Allocation failed for pl_info", 431 __func__); 432 goto attach_fail1; 433 } 434 435 pl_dev->pl_info = &pl_info_lnx->info; 436 pl_dev->name = WLANDEV_BASENAME; 437 proc_name = pl_dev->name; 438 439 if (!pl_dev->pl_funcs) 440 pl_dev->pl_funcs = &ol_pl_funcs; 441 442 /* 443 * Valid for both direct attach and offload architecture 444 */ 445 pl_dev->pl_funcs->pktlog_init(scn); 446 } else { 447 qdf_err("pl_dev is NULL"); 448 return -EINVAL; 449 } 450 451 /* 452 * initialize log info 453 * might be good to move to pktlog_init 454 */ 455 /* pl_dev->tgt_pktlog_alloced = false; */ 456 pl_info_lnx->proc_entry = NULL; 457 pl_info_lnx->sysctl_header = NULL; 458 459 proc_entry = proc_create_data(proc_name, PKTLOG_PROC_PERM, 460 g_pktlog_pde, &pktlog_fops, 461 &pl_info_lnx->info); 462 463 if (!proc_entry) { 464 qdf_info(PKTLOG_TAG "create_proc_entry failed for %s", proc_name); 465 goto attach_fail1; 466 } 467 468 pl_info_lnx->proc_entry = proc_entry; 469 470 if (pktlog_sysctl_register(scn)) { 471 qdf_nofl_info(PKTLOG_TAG "sysctl register failed for %s", 472 proc_name); 473 goto attach_fail2; 474 } 475 476 return 0; 477 478 attach_fail2: 479 remove_proc_entry(proc_name, g_pktlog_pde); 480 481 attach_fail1: 482 if (pl_dev) 483 kfree(pl_dev->pl_info); 484 485 return -EINVAL; 486 } 487 488 static void pktlog_sysctl_unregister(struct pktlog_dev_t *pl_dev) 489 { 490 struct ath_pktlog_info_lnx *pl_info_lnx; 491 492 if (!pl_dev) { 493 qdf_info("Invalid pktlog context"); 494 ASSERT(0); 495 return; 496 } 497 498 pl_info_lnx = (pl_dev) ? PL_INFO_LNX(pl_dev->pl_info) : 499 PL_INFO_LNX(g_pktlog_info); 500 501 if (pl_info_lnx->sysctl_header) { 502 unregister_sysctl_table(pl_info_lnx->sysctl_header); 503 pl_info_lnx->sysctl_header = NULL; 504 } 505 } 506 507 static void pktlog_detach(struct hif_opaque_softc *scn) 508 { 509 struct ath_pktlog_info *pl_info; 510 struct pktlog_dev_t *pl_dev = get_pktlog_handle(); 511 512 qdf_info("detach pktlog resources"); 513 if (!pl_dev) { 514 qdf_info("Invalid pktlog context"); 515 ASSERT(0); 516 return; 517 } 518 519 pl_info = pl_dev->pl_info; 520 if (!pl_info) { 521 qdf_print("Invalid pktlog handle"); 522 ASSERT(0); 523 return; 524 } 525 mutex_lock(&pl_info->pktlog_mutex); 526 remove_proc_entry(WLANDEV_BASENAME, g_pktlog_pde); 527 pktlog_sysctl_unregister(pl_dev); 528 529 qdf_spin_lock_bh(&pl_info->log_lock); 530 531 if (pl_info->buf) { 532 pktlog_release_buf(scn); 533 pl_dev->tgt_pktlog_alloced = false; 534 } 535 qdf_spin_unlock_bh(&pl_info->log_lock); 536 mutex_unlock(&pl_info->pktlog_mutex); 537 pktlog_cleanup(pl_info); 538 539 if (pl_dev) { 540 kfree(pl_info); 541 pl_dev->pl_info = NULL; 542 } 543 } 544 545 static int __pktlog_open(struct inode *i, struct file *f) 546 { 547 struct hif_opaque_softc *scn; 548 struct pktlog_dev_t *pl_dev; 549 struct ath_pktlog_info *pl_info; 550 struct ath_pktlog_info_lnx *pl_info_lnx; 551 int ret = 0; 552 553 PKTLOG_MOD_INC_USE_COUNT; 554 scn = cds_get_context(QDF_MODULE_ID_HIF); 555 if (!scn) { 556 qdf_print("Invalid scn context"); 557 ASSERT(0); 558 return -EINVAL; 559 } 560 561 pl_dev = get_pktlog_handle(); 562 563 if (!pl_dev) { 564 qdf_print("Invalid pktlog handle"); 565 ASSERT(0); 566 return -ENODEV; 567 } 568 569 pl_info = pl_dev->pl_info; 570 571 if (!pl_info) { 572 qdf_err("pl_info NULL"); 573 return -EINVAL; 574 } 575 576 mutex_lock(&pl_info->pktlog_mutex); 577 pl_info_lnx = (pl_dev) ? PL_INFO_LNX(pl_dev->pl_info) : 578 PL_INFO_LNX(g_pktlog_info); 579 580 if (!pl_info_lnx->sysctl_header) { 581 mutex_unlock(&pl_info->pktlog_mutex); 582 qdf_print("pktlog sysctl is unergistered"); 583 ASSERT(0); 584 return -EINVAL; 585 } 586 587 if (pl_info->curr_pkt_state != PKTLOG_OPR_NOT_IN_PROGRESS) { 588 mutex_unlock(&pl_info->pktlog_mutex); 589 qdf_print("plinfo state (%d) != PKTLOG_OPR_NOT_IN_PROGRESS", 590 pl_info->curr_pkt_state); 591 return -EBUSY; 592 } 593 594 pl_info->curr_pkt_state = PKTLOG_OPR_IN_PROGRESS_READ_START; 595 596 pl_info->init_saved_state = pl_info->log_state; 597 if (!pl_info->log_state) { 598 /* Pktlog is already disabled. 599 * Proceed to read directly. 600 */ 601 pl_info->curr_pkt_state = 602 PKTLOG_OPR_IN_PROGRESS_READ_START_PKTLOG_DISABLED; 603 mutex_unlock(&pl_info->pktlog_mutex); 604 return ret; 605 } 606 /* Disbable the pktlog internally. */ 607 ret = pl_dev->pl_funcs->pktlog_disable(scn); 608 pl_info->log_state = 0; 609 pl_info->curr_pkt_state = 610 PKTLOG_OPR_IN_PROGRESS_READ_START_PKTLOG_DISABLED; 611 mutex_unlock(&pl_info->pktlog_mutex); 612 return ret; 613 } 614 615 static int pktlog_open(struct inode *i, struct file *f) 616 { 617 struct qdf_op_sync *op_sync; 618 int errno; 619 620 errno = qdf_op_protect(&op_sync); 621 if (errno) 622 return errno; 623 624 errno = __pktlog_open(i, f); 625 626 qdf_op_unprotect(op_sync); 627 628 return errno; 629 } 630 631 static int __pktlog_release(struct inode *i, struct file *f) 632 { 633 struct hif_opaque_softc *scn; 634 struct pktlog_dev_t *pl_dev; 635 struct ath_pktlog_info *pl_info; 636 struct ath_pktlog_info_lnx *pl_info_lnx; 637 int ret = 0; 638 639 PKTLOG_MOD_DEC_USE_COUNT; 640 scn = cds_get_context(QDF_MODULE_ID_HIF); 641 if (!scn) { 642 qdf_print("Invalid scn context"); 643 ASSERT(0); 644 return -EINVAL; 645 } 646 647 pl_dev = get_pktlog_handle(); 648 649 if (!pl_dev) { 650 qdf_print("Invalid pktlog handle"); 651 ASSERT(0); 652 return -ENODEV; 653 } 654 655 pl_info = pl_dev->pl_info; 656 657 if (!pl_info) { 658 qdf_print("Invalid pktlog info"); 659 ASSERT(0); 660 return -EINVAL; 661 } 662 663 mutex_lock(&pl_info->pktlog_mutex); 664 pl_info_lnx = (pl_dev) ? PL_INFO_LNX(pl_dev->pl_info) : 665 PL_INFO_LNX(g_pktlog_info); 666 667 if (!pl_info_lnx->sysctl_header) { 668 pl_info->curr_pkt_state = PKTLOG_OPR_NOT_IN_PROGRESS; 669 mutex_unlock(&pl_info->pktlog_mutex); 670 qdf_print("pktlog sysctl is unergistered"); 671 ASSERT(0); 672 return -EINVAL; 673 } 674 pl_info->curr_pkt_state = PKTLOG_OPR_IN_PROGRESS_READ_COMPLETE; 675 /*clear pktlog buffer.*/ 676 pktlog_clearbuff(scn, true); 677 pl_info->log_state = pl_info->init_saved_state; 678 pl_info->init_saved_state = 0; 679 680 /*Enable pktlog again*/ 681 ret = __pktlog_enable( 682 (struct hif_opaque_softc *)scn, pl_info->log_state, 683 cds_is_packet_log_enabled(), 0, 1); 684 685 pl_info->curr_pkt_state = PKTLOG_OPR_NOT_IN_PROGRESS; 686 mutex_unlock(&pl_info->pktlog_mutex); 687 if (ret != 0) 688 qdf_print("pktlog cannot be enabled. ret value %d", ret); 689 690 return ret; 691 } 692 693 static int pktlog_release(struct inode *i, struct file *f) 694 { 695 struct qdf_op_sync *op_sync; 696 int errno; 697 698 errno = qdf_op_protect(&op_sync); 699 if (errno) 700 return errno; 701 702 errno = __pktlog_release(i, f); 703 704 qdf_op_unprotect(op_sync); 705 706 return errno; 707 } 708 709 #ifndef MIN 710 #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 711 #endif 712 713 /** 714 * pktlog_read_proc_entry() - This function is used to read data from the 715 * proc entry into the readers buffer 716 * @buf: Readers buffer 717 * @nbytes: Number of bytes to read 718 * @ppos: Offset within the drivers buffer 719 * @pl_info: Packet log information pointer 720 * @read_complete: Boolean value indication whether read is complete 721 * 722 * This function is used to read data from the proc entry into the readers 723 * buffer. Its functionality is similar to 'pktlog_read' which does 724 * copy to user to the user space buffer 725 * 726 * Return: Number of bytes read from the buffer 727 * 728 */ 729 ssize_t 730 pktlog_read_proc_entry(char *buf, size_t nbytes, loff_t *ppos, 731 struct ath_pktlog_info *pl_info, bool *read_complete) 732 { 733 size_t bufhdr_size; 734 size_t count = 0, ret_val = 0; 735 int rem_len; 736 int start_offset, end_offset; 737 int fold_offset, ppos_data, cur_rd_offset, cur_wr_offset; 738 struct ath_pktlog_buf *log_buf; 739 740 qdf_spin_lock_bh(&pl_info->log_lock); 741 log_buf = pl_info->buf; 742 743 *read_complete = false; 744 745 if (!log_buf) { 746 *read_complete = true; 747 qdf_spin_unlock_bh(&pl_info->log_lock); 748 return 0; 749 } 750 751 if (*ppos == 0 && pl_info->log_state) { 752 pl_info->saved_state = pl_info->log_state; 753 pl_info->log_state = 0; 754 } 755 756 bufhdr_size = sizeof(log_buf->bufhdr); 757 758 /* copy valid log entries from circular buffer into user space */ 759 rem_len = nbytes; 760 count = 0; 761 762 if (*ppos < bufhdr_size) { 763 count = MIN((bufhdr_size - *ppos), rem_len); 764 qdf_mem_copy(buf, ((char *)&log_buf->bufhdr) + *ppos, 765 count); 766 rem_len -= count; 767 ret_val += count; 768 } 769 770 start_offset = log_buf->rd_offset; 771 cur_wr_offset = log_buf->wr_offset; 772 773 if ((rem_len == 0) || (start_offset < 0)) 774 goto rd_done; 775 776 fold_offset = -1; 777 cur_rd_offset = start_offset; 778 779 /* Find the last offset and fold-offset if the buffer is folded */ 780 do { 781 struct ath_pktlog_hdr *log_hdr; 782 int log_data_offset; 783 784 log_hdr = (struct ath_pktlog_hdr *) (log_buf->log_data + 785 cur_rd_offset); 786 787 log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr); 788 789 if ((fold_offset == -1) 790 && ((pl_info->buf_size - log_data_offset) 791 <= log_hdr->size)) 792 fold_offset = log_data_offset - 1; 793 794 PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf, pl_info->buf_size); 795 796 if ((fold_offset == -1) && (cur_rd_offset == 0) 797 && (cur_rd_offset != cur_wr_offset)) 798 fold_offset = log_data_offset + log_hdr->size - 1; 799 800 end_offset = log_data_offset + log_hdr->size - 1; 801 } while (cur_rd_offset != cur_wr_offset); 802 803 ppos_data = *ppos + ret_val - bufhdr_size + start_offset; 804 805 if (fold_offset == -1) { 806 if (ppos_data > end_offset) 807 goto rd_done; 808 809 count = MIN(rem_len, (end_offset - ppos_data + 1)); 810 qdf_mem_copy(buf + ret_val, 811 log_buf->log_data + ppos_data, 812 count); 813 ret_val += count; 814 rem_len -= count; 815 } else { 816 if (ppos_data <= fold_offset) { 817 count = MIN(rem_len, (fold_offset - ppos_data + 1)); 818 qdf_mem_copy(buf + ret_val, 819 log_buf->log_data + ppos_data, 820 count); 821 ret_val += count; 822 rem_len -= count; 823 } 824 825 if (rem_len == 0) 826 goto rd_done; 827 828 ppos_data = 829 *ppos + ret_val - (bufhdr_size + 830 (fold_offset - start_offset + 1)); 831 832 if (ppos_data <= end_offset) { 833 count = MIN(rem_len, (end_offset - ppos_data + 1)); 834 qdf_mem_copy(buf + ret_val, 835 log_buf->log_data + ppos_data, 836 count); 837 ret_val += count; 838 rem_len -= count; 839 } 840 } 841 842 rd_done: 843 if ((ret_val < nbytes) && pl_info->saved_state) { 844 pl_info->log_state = pl_info->saved_state; 845 pl_info->saved_state = 0; 846 } 847 *ppos += ret_val; 848 849 if (ret_val == 0) { 850 /* Write pointer might have been updated during the read. 851 * So, if some data is written into, lets not reset the pointers 852 * We can continue to read from the offset position 853 */ 854 if (cur_wr_offset != log_buf->wr_offset) { 855 *read_complete = false; 856 } else { 857 pl_info->buf->rd_offset = -1; 858 pl_info->buf->wr_offset = 0; 859 pl_info->buf->bytes_written = 0; 860 pl_info->buf->offset = PKTLOG_READ_OFFSET; 861 *read_complete = true; 862 } 863 } 864 qdf_spin_unlock_bh(&pl_info->log_lock); 865 return ret_val; 866 } 867 868 static ssize_t 869 __pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) 870 { 871 size_t bufhdr_size; 872 size_t count = 0, ret_val = 0; 873 int rem_len; 874 int start_offset, end_offset; 875 int fold_offset, ppos_data, cur_rd_offset; 876 struct ath_pktlog_info *pl_info; 877 struct ath_pktlog_buf *log_buf; 878 879 pl_info = PDE_DATA(file->f_path.dentry->d_inode); 880 if (!pl_info) 881 return 0; 882 883 qdf_spin_lock_bh(&pl_info->log_lock); 884 log_buf = pl_info->buf; 885 886 if (!log_buf) { 887 qdf_spin_unlock_bh(&pl_info->log_lock); 888 return 0; 889 } 890 891 if (pl_info->log_state) { 892 /* Read is not allowed when write is going on 893 * When issuing cat command, ensure to send 894 * pktlog disable command first. 895 */ 896 qdf_spin_unlock_bh(&pl_info->log_lock); 897 return -EINVAL; 898 } 899 900 if (*ppos == 0 && pl_info->log_state) { 901 pl_info->saved_state = pl_info->log_state; 902 pl_info->log_state = 0; 903 } 904 905 bufhdr_size = sizeof(log_buf->bufhdr); 906 907 /* copy valid log entries from circular buffer into user space */ 908 909 rem_len = nbytes; 910 count = 0; 911 912 if (*ppos < bufhdr_size) { 913 count = QDF_MIN((bufhdr_size - *ppos), rem_len); 914 qdf_spin_unlock_bh(&pl_info->log_lock); 915 if (copy_to_user(buf, ((char *)&log_buf->bufhdr) + *ppos, 916 count)) { 917 return -EFAULT; 918 } 919 rem_len -= count; 920 ret_val += count; 921 qdf_spin_lock_bh(&pl_info->log_lock); 922 } 923 924 start_offset = log_buf->rd_offset; 925 926 if ((rem_len == 0) || (start_offset < 0)) 927 goto rd_done; 928 929 fold_offset = -1; 930 cur_rd_offset = start_offset; 931 932 /* Find the last offset and fold-offset if the buffer is folded */ 933 do { 934 struct ath_pktlog_hdr *log_hdr; 935 int log_data_offset; 936 937 log_hdr = (struct ath_pktlog_hdr *)(log_buf->log_data + 938 cur_rd_offset); 939 940 log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr); 941 942 if ((fold_offset == -1) 943 && ((pl_info->buf_size - log_data_offset) 944 <= log_hdr->size)) 945 fold_offset = log_data_offset - 1; 946 947 PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf, pl_info->buf_size); 948 949 if ((fold_offset == -1) && (cur_rd_offset == 0) 950 && (cur_rd_offset != log_buf->wr_offset)) 951 fold_offset = log_data_offset + log_hdr->size - 1; 952 953 end_offset = log_data_offset + log_hdr->size - 1; 954 } while (cur_rd_offset != log_buf->wr_offset); 955 956 ppos_data = *ppos + ret_val - bufhdr_size + start_offset; 957 958 if (fold_offset == -1) { 959 if (ppos_data > end_offset) 960 goto rd_done; 961 962 count = QDF_MIN(rem_len, (end_offset - ppos_data + 1)); 963 qdf_spin_unlock_bh(&pl_info->log_lock); 964 965 if (copy_to_user(buf + ret_val, 966 log_buf->log_data + ppos_data, count)) { 967 return -EFAULT; 968 } 969 970 ret_val += count; 971 rem_len -= count; 972 qdf_spin_lock_bh(&pl_info->log_lock); 973 } else { 974 if (ppos_data <= fold_offset) { 975 count = QDF_MIN(rem_len, (fold_offset - ppos_data + 1)); 976 qdf_spin_unlock_bh(&pl_info->log_lock); 977 if (copy_to_user(buf + ret_val, 978 log_buf->log_data + ppos_data, 979 count)) { 980 return -EFAULT; 981 } 982 ret_val += count; 983 rem_len -= count; 984 qdf_spin_lock_bh(&pl_info->log_lock); 985 } 986 987 if (rem_len == 0) 988 goto rd_done; 989 990 ppos_data = 991 *ppos + ret_val - (bufhdr_size + 992 (fold_offset - start_offset + 1)); 993 994 if (ppos_data <= end_offset) { 995 count = QDF_MIN(rem_len, (end_offset - ppos_data + 1)); 996 qdf_spin_unlock_bh(&pl_info->log_lock); 997 if (copy_to_user(buf + ret_val, 998 log_buf->log_data + ppos_data, 999 count)) { 1000 return -EFAULT; 1001 } 1002 ret_val += count; 1003 rem_len -= count; 1004 qdf_spin_lock_bh(&pl_info->log_lock); 1005 } 1006 } 1007 1008 rd_done: 1009 if ((ret_val < nbytes) && pl_info->saved_state) { 1010 pl_info->log_state = pl_info->saved_state; 1011 pl_info->saved_state = 0; 1012 } 1013 *ppos += ret_val; 1014 1015 qdf_spin_unlock_bh(&pl_info->log_lock); 1016 return ret_val; 1017 } 1018 1019 static ssize_t 1020 pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) 1021 { 1022 struct ath_pktlog_info *info = PDE_DATA(file->f_path.dentry->d_inode); 1023 struct qdf_op_sync *op_sync; 1024 ssize_t err_size; 1025 1026 if (!info) 1027 return 0; 1028 1029 err_size = qdf_op_protect(&op_sync); 1030 if (err_size) 1031 return err_size; 1032 1033 mutex_lock(&info->pktlog_mutex); 1034 err_size = __pktlog_read(file, buf, nbytes, ppos); 1035 mutex_unlock(&info->pktlog_mutex); 1036 1037 qdf_op_unprotect(op_sync); 1038 1039 return err_size; 1040 } 1041 1042 int pktlogmod_init(void *context) 1043 { 1044 int ret; 1045 1046 qdf_info("Initialize pkt_log module"); 1047 /* create the proc directory entry */ 1048 g_pktlog_pde = proc_mkdir(PKTLOG_PROC_DIR, NULL); 1049 1050 if (!g_pktlog_pde) { 1051 qdf_info(PKTLOG_TAG "proc_mkdir failed"); 1052 return -EPERM; 1053 } 1054 1055 /* Attach packet log */ 1056 ret = pktlog_attach((struct hif_opaque_softc *)context); 1057 1058 /* If packet log init failed */ 1059 if (ret) { 1060 qdf_err("pktlog_attach failed"); 1061 goto attach_fail; 1062 } 1063 1064 return ret; 1065 1066 attach_fail: 1067 remove_proc_entry(PKTLOG_PROC_DIR, NULL); 1068 g_pktlog_pde = NULL; 1069 1070 return ret; 1071 } 1072 1073 void pktlogmod_exit(void *context) 1074 { 1075 qdf_info("pkt_log module cleanup"); 1076 if (!g_pktlog_pde) { 1077 qdf_err("g_pktlog_pde is NULL"); 1078 return; 1079 } 1080 1081 pktlog_detach((struct hif_opaque_softc *)context); 1082 1083 /* 1084 * pdev kill needs to be implemented 1085 */ 1086 remove_proc_entry(PKTLOG_PROC_DIR, NULL); 1087 g_pktlog_pde = NULL; 1088 } 1089 #endif 1090