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