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