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