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 if (!pl_info) { 510 qdf_print("%s: Invalid pktlog handle", __func__); 511 ASSERT(0); 512 return; 513 } 514 mutex_lock(&pl_info->pktlog_mutex); 515 remove_proc_entry(WLANDEV_BASENAME, g_pktlog_pde); 516 pktlog_sysctl_unregister(pl_dev); 517 518 qdf_spin_lock_bh(&pl_info->log_lock); 519 520 if (pl_info->buf) { 521 pktlog_release_buf(scn); 522 pl_dev->tgt_pktlog_alloced = false; 523 } 524 qdf_spin_unlock_bh(&pl_info->log_lock); 525 mutex_unlock(&pl_info->pktlog_mutex); 526 pktlog_cleanup(pl_info); 527 528 if (pl_dev) { 529 kfree(pl_info); 530 pl_dev->pl_info = NULL; 531 } 532 } 533 534 static int __pktlog_open(struct inode *i, struct file *f) 535 { 536 struct hif_opaque_softc *scn; 537 struct pktlog_dev_t *pl_dev; 538 struct ath_pktlog_info *pl_info; 539 struct ath_pktlog_info_lnx *pl_info_lnx; 540 int ret = 0; 541 542 PKTLOG_MOD_INC_USE_COUNT; 543 scn = cds_get_context(QDF_MODULE_ID_HIF); 544 if (!scn) { 545 qdf_print("%s: Invalid scn context", __func__); 546 ASSERT(0); 547 return -EINVAL; 548 } 549 550 pl_dev = get_pktlog_handle(); 551 552 if (!pl_dev) { 553 qdf_print("%s: Invalid pktlog handle", __func__); 554 ASSERT(0); 555 return -ENODEV; 556 } 557 558 pl_info = pl_dev->pl_info; 559 560 if (!pl_info) { 561 qdf_nofl_err("%s: pl_info NULL", __func__); 562 return -EINVAL; 563 } 564 565 mutex_lock(&pl_info->pktlog_mutex); 566 pl_info_lnx = (pl_dev) ? PL_INFO_LNX(pl_dev->pl_info) : 567 PL_INFO_LNX(g_pktlog_info); 568 569 if (!pl_info_lnx->sysctl_header) { 570 mutex_unlock(&pl_info->pktlog_mutex); 571 qdf_print("%s: pktlog sysctl is unergistered.", __func__); 572 ASSERT(0); 573 return -EINVAL; 574 } 575 576 if (pl_info->curr_pkt_state != PKTLOG_OPR_NOT_IN_PROGRESS) { 577 mutex_unlock(&pl_info->pktlog_mutex); 578 qdf_print("%s: plinfo state (%d) != PKTLOG_OPR_NOT_IN_PROGRESS", 579 __func__, pl_info->curr_pkt_state); 580 return -EBUSY; 581 } 582 583 pl_info->curr_pkt_state = PKTLOG_OPR_IN_PROGRESS_READ_START; 584 585 pl_info->init_saved_state = pl_info->log_state; 586 if (!pl_info->log_state) { 587 /* Pktlog is already disabled. 588 * Proceed to read directly. 589 */ 590 pl_info->curr_pkt_state = 591 PKTLOG_OPR_IN_PROGRESS_READ_START_PKTLOG_DISABLED; 592 mutex_unlock(&pl_info->pktlog_mutex); 593 return ret; 594 } 595 /* Disbable the pktlog internally. */ 596 ret = pl_dev->pl_funcs->pktlog_disable(scn); 597 pl_info->log_state = 0; 598 pl_info->curr_pkt_state = 599 PKTLOG_OPR_IN_PROGRESS_READ_START_PKTLOG_DISABLED; 600 mutex_unlock(&pl_info->pktlog_mutex); 601 return ret; 602 } 603 604 static int pktlog_open(struct inode *i, struct file *f) 605 { 606 struct qdf_op_sync *op_sync; 607 int errno; 608 609 errno = qdf_op_protect(&op_sync); 610 if (errno) 611 return errno; 612 613 errno = __pktlog_open(i, f); 614 615 qdf_op_unprotect(op_sync); 616 617 return errno; 618 } 619 620 static int __pktlog_release(struct inode *i, struct file *f) 621 { 622 struct hif_opaque_softc *scn; 623 struct pktlog_dev_t *pl_dev; 624 struct ath_pktlog_info *pl_info; 625 struct ath_pktlog_info_lnx *pl_info_lnx; 626 int ret = 0; 627 628 PKTLOG_MOD_DEC_USE_COUNT; 629 scn = cds_get_context(QDF_MODULE_ID_HIF); 630 if (!scn) { 631 qdf_print("%s: Invalid scn context", __func__); 632 ASSERT(0); 633 return -EINVAL; 634 } 635 636 pl_dev = get_pktlog_handle(); 637 638 if (!pl_dev) { 639 qdf_print("%s: Invalid pktlog handle", __func__); 640 ASSERT(0); 641 return -ENODEV; 642 } 643 644 pl_info = pl_dev->pl_info; 645 646 if (!pl_info) { 647 qdf_print("%s: Invalid pktlog info", __func__); 648 ASSERT(0); 649 return -EINVAL; 650 } 651 652 mutex_lock(&pl_info->pktlog_mutex); 653 pl_info_lnx = (pl_dev) ? PL_INFO_LNX(pl_dev->pl_info) : 654 PL_INFO_LNX(g_pktlog_info); 655 656 if (!pl_info_lnx->sysctl_header) { 657 pl_info->curr_pkt_state = PKTLOG_OPR_NOT_IN_PROGRESS; 658 mutex_unlock(&pl_info->pktlog_mutex); 659 qdf_print("%s: pktlog sysctl is unergistered.", __func__); 660 ASSERT(0); 661 return -EINVAL; 662 } 663 pl_info->curr_pkt_state = PKTLOG_OPR_IN_PROGRESS_READ_COMPLETE; 664 /*clear pktlog buffer.*/ 665 pktlog_clearbuff(scn, true); 666 pl_info->log_state = pl_info->init_saved_state; 667 pl_info->init_saved_state = 0; 668 669 /*Enable pktlog again*/ 670 ret = __pktlog_enable( 671 (struct hif_opaque_softc *)scn, pl_info->log_state, 672 cds_is_packet_log_enabled(), 0, 1); 673 674 pl_info->curr_pkt_state = PKTLOG_OPR_NOT_IN_PROGRESS; 675 mutex_unlock(&pl_info->pktlog_mutex); 676 if (ret != 0) 677 qdf_print("%s: pktlog cannot be enabled. ret value %d", 678 __func__, ret); 679 680 return ret; 681 } 682 683 static int pktlog_release(struct inode *i, struct file *f) 684 { 685 struct qdf_op_sync *op_sync; 686 int errno; 687 688 errno = qdf_op_protect(&op_sync); 689 if (errno) 690 return errno; 691 692 errno = __pktlog_release(i, f); 693 694 qdf_op_unprotect(op_sync); 695 696 return errno; 697 } 698 699 #ifndef MIN 700 #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 701 #endif 702 703 /** 704 * pktlog_read_proc_entry() - This function is used to read data from the 705 * proc entry into the readers buffer 706 * @buf: Readers buffer 707 * @nbytes: Number of bytes to read 708 * @ppos: Offset within the drivers buffer 709 * @pl_info: Packet log information pointer 710 * @read_complete: Boolean value indication whether read is complete 711 * 712 * This function is used to read data from the proc entry into the readers 713 * buffer. Its functionality is similar to 'pktlog_read' which does 714 * copy to user to the user space buffer 715 * 716 * Return: Number of bytes read from the buffer 717 * 718 */ 719 ssize_t 720 pktlog_read_proc_entry(char *buf, size_t nbytes, loff_t *ppos, 721 struct ath_pktlog_info *pl_info, bool *read_complete) 722 { 723 size_t bufhdr_size; 724 size_t count = 0, ret_val = 0; 725 int rem_len; 726 int start_offset, end_offset; 727 int fold_offset, ppos_data, cur_rd_offset, cur_wr_offset; 728 struct ath_pktlog_buf *log_buf; 729 730 qdf_spin_lock_bh(&pl_info->log_lock); 731 log_buf = pl_info->buf; 732 733 *read_complete = false; 734 735 if (!log_buf) { 736 *read_complete = true; 737 qdf_spin_unlock_bh(&pl_info->log_lock); 738 return 0; 739 } 740 741 if (*ppos == 0 && pl_info->log_state) { 742 pl_info->saved_state = pl_info->log_state; 743 pl_info->log_state = 0; 744 } 745 746 bufhdr_size = sizeof(log_buf->bufhdr); 747 748 /* copy valid log entries from circular buffer into user space */ 749 rem_len = nbytes; 750 count = 0; 751 752 if (*ppos < bufhdr_size) { 753 count = MIN((bufhdr_size - *ppos), rem_len); 754 qdf_mem_copy(buf, ((char *)&log_buf->bufhdr) + *ppos, 755 count); 756 rem_len -= count; 757 ret_val += count; 758 } 759 760 start_offset = log_buf->rd_offset; 761 cur_wr_offset = log_buf->wr_offset; 762 763 if ((rem_len == 0) || (start_offset < 0)) 764 goto rd_done; 765 766 fold_offset = -1; 767 cur_rd_offset = start_offset; 768 769 /* Find the last offset and fold-offset if the buffer is folded */ 770 do { 771 struct ath_pktlog_hdr *log_hdr; 772 int log_data_offset; 773 774 log_hdr = (struct ath_pktlog_hdr *) (log_buf->log_data + 775 cur_rd_offset); 776 777 log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr); 778 779 if ((fold_offset == -1) 780 && ((pl_info->buf_size - log_data_offset) 781 <= log_hdr->size)) 782 fold_offset = log_data_offset - 1; 783 784 PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf, pl_info->buf_size); 785 786 if ((fold_offset == -1) && (cur_rd_offset == 0) 787 && (cur_rd_offset != cur_wr_offset)) 788 fold_offset = log_data_offset + log_hdr->size - 1; 789 790 end_offset = log_data_offset + log_hdr->size - 1; 791 } while (cur_rd_offset != cur_wr_offset); 792 793 ppos_data = *ppos + ret_val - bufhdr_size + start_offset; 794 795 if (fold_offset == -1) { 796 if (ppos_data > end_offset) 797 goto rd_done; 798 799 count = MIN(rem_len, (end_offset - ppos_data + 1)); 800 qdf_mem_copy(buf + ret_val, 801 log_buf->log_data + ppos_data, 802 count); 803 ret_val += count; 804 rem_len -= count; 805 } else { 806 if (ppos_data <= fold_offset) { 807 count = MIN(rem_len, (fold_offset - ppos_data + 1)); 808 qdf_mem_copy(buf + ret_val, 809 log_buf->log_data + ppos_data, 810 count); 811 ret_val += count; 812 rem_len -= count; 813 } 814 815 if (rem_len == 0) 816 goto rd_done; 817 818 ppos_data = 819 *ppos + ret_val - (bufhdr_size + 820 (fold_offset - start_offset + 1)); 821 822 if (ppos_data <= end_offset) { 823 count = MIN(rem_len, (end_offset - ppos_data + 1)); 824 qdf_mem_copy(buf + ret_val, 825 log_buf->log_data + ppos_data, 826 count); 827 ret_val += count; 828 rem_len -= count; 829 } 830 } 831 832 rd_done: 833 if ((ret_val < nbytes) && pl_info->saved_state) { 834 pl_info->log_state = pl_info->saved_state; 835 pl_info->saved_state = 0; 836 } 837 *ppos += ret_val; 838 839 if (ret_val == 0) { 840 /* Write pointer might have been updated during the read. 841 * So, if some data is written into, lets not reset the pointers 842 * We can continue to read from the offset position 843 */ 844 if (cur_wr_offset != log_buf->wr_offset) { 845 *read_complete = false; 846 } else { 847 pl_info->buf->rd_offset = -1; 848 pl_info->buf->wr_offset = 0; 849 pl_info->buf->bytes_written = 0; 850 pl_info->buf->offset = PKTLOG_READ_OFFSET; 851 *read_complete = true; 852 } 853 } 854 qdf_spin_unlock_bh(&pl_info->log_lock); 855 return ret_val; 856 } 857 858 static ssize_t 859 __pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) 860 { 861 size_t bufhdr_size; 862 size_t count = 0, ret_val = 0; 863 int rem_len; 864 int start_offset, end_offset; 865 int fold_offset, ppos_data, cur_rd_offset; 866 struct ath_pktlog_info *pl_info; 867 struct ath_pktlog_buf *log_buf; 868 869 pl_info = PDE_DATA(file->f_path.dentry->d_inode); 870 if (!pl_info) 871 return 0; 872 873 qdf_spin_lock_bh(&pl_info->log_lock); 874 log_buf = pl_info->buf; 875 876 if (!log_buf) { 877 qdf_spin_unlock_bh(&pl_info->log_lock); 878 return 0; 879 } 880 881 if (pl_info->log_state) { 882 /* Read is not allowed when write is going on 883 * When issuing cat command, ensure to send 884 * pktlog disable command first. 885 */ 886 qdf_spin_unlock_bh(&pl_info->log_lock); 887 return -EINVAL; 888 } 889 890 if (*ppos == 0 && pl_info->log_state) { 891 pl_info->saved_state = pl_info->log_state; 892 pl_info->log_state = 0; 893 } 894 895 bufhdr_size = sizeof(log_buf->bufhdr); 896 897 /* copy valid log entries from circular buffer into user space */ 898 899 rem_len = nbytes; 900 count = 0; 901 902 if (*ppos < bufhdr_size) { 903 count = QDF_MIN((bufhdr_size - *ppos), rem_len); 904 qdf_spin_unlock_bh(&pl_info->log_lock); 905 if (copy_to_user(buf, ((char *)&log_buf->bufhdr) + *ppos, 906 count)) { 907 return -EFAULT; 908 } 909 rem_len -= count; 910 ret_val += count; 911 qdf_spin_lock_bh(&pl_info->log_lock); 912 } 913 914 start_offset = log_buf->rd_offset; 915 916 if ((rem_len == 0) || (start_offset < 0)) 917 goto rd_done; 918 919 fold_offset = -1; 920 cur_rd_offset = start_offset; 921 922 /* Find the last offset and fold-offset if the buffer is folded */ 923 do { 924 struct ath_pktlog_hdr *log_hdr; 925 int log_data_offset; 926 927 log_hdr = (struct ath_pktlog_hdr *)(log_buf->log_data + 928 cur_rd_offset); 929 930 log_data_offset = cur_rd_offset + sizeof(struct ath_pktlog_hdr); 931 932 if ((fold_offset == -1) 933 && ((pl_info->buf_size - log_data_offset) 934 <= log_hdr->size)) 935 fold_offset = log_data_offset - 1; 936 937 PKTLOG_MOV_RD_IDX(cur_rd_offset, log_buf, pl_info->buf_size); 938 939 if ((fold_offset == -1) && (cur_rd_offset == 0) 940 && (cur_rd_offset != log_buf->wr_offset)) 941 fold_offset = log_data_offset + log_hdr->size - 1; 942 943 end_offset = log_data_offset + log_hdr->size - 1; 944 } while (cur_rd_offset != log_buf->wr_offset); 945 946 ppos_data = *ppos + ret_val - bufhdr_size + start_offset; 947 948 if (fold_offset == -1) { 949 if (ppos_data > end_offset) 950 goto rd_done; 951 952 count = QDF_MIN(rem_len, (end_offset - ppos_data + 1)); 953 qdf_spin_unlock_bh(&pl_info->log_lock); 954 955 if (copy_to_user(buf + ret_val, 956 log_buf->log_data + ppos_data, count)) { 957 return -EFAULT; 958 } 959 960 ret_val += count; 961 rem_len -= count; 962 qdf_spin_lock_bh(&pl_info->log_lock); 963 } else { 964 if (ppos_data <= fold_offset) { 965 count = QDF_MIN(rem_len, (fold_offset - ppos_data + 1)); 966 qdf_spin_unlock_bh(&pl_info->log_lock); 967 if (copy_to_user(buf + ret_val, 968 log_buf->log_data + ppos_data, 969 count)) { 970 return -EFAULT; 971 } 972 ret_val += count; 973 rem_len -= count; 974 qdf_spin_lock_bh(&pl_info->log_lock); 975 } 976 977 if (rem_len == 0) 978 goto rd_done; 979 980 ppos_data = 981 *ppos + ret_val - (bufhdr_size + 982 (fold_offset - start_offset + 1)); 983 984 if (ppos_data <= end_offset) { 985 count = QDF_MIN(rem_len, (end_offset - ppos_data + 1)); 986 qdf_spin_unlock_bh(&pl_info->log_lock); 987 if (copy_to_user(buf + ret_val, 988 log_buf->log_data + ppos_data, 989 count)) { 990 return -EFAULT; 991 } 992 ret_val += count; 993 rem_len -= count; 994 qdf_spin_lock_bh(&pl_info->log_lock); 995 } 996 } 997 998 rd_done: 999 if ((ret_val < nbytes) && pl_info->saved_state) { 1000 pl_info->log_state = pl_info->saved_state; 1001 pl_info->saved_state = 0; 1002 } 1003 *ppos += ret_val; 1004 1005 qdf_spin_unlock_bh(&pl_info->log_lock); 1006 return ret_val; 1007 } 1008 1009 static ssize_t 1010 pktlog_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) 1011 { 1012 struct ath_pktlog_info *info = PDE_DATA(file->f_path.dentry->d_inode); 1013 struct qdf_op_sync *op_sync; 1014 ssize_t err_size; 1015 1016 if (!info) 1017 return 0; 1018 1019 err_size = qdf_op_protect(&op_sync); 1020 if (err_size) 1021 return err_size; 1022 1023 mutex_lock(&info->pktlog_mutex); 1024 err_size = __pktlog_read(file, buf, nbytes, ppos); 1025 mutex_unlock(&info->pktlog_mutex); 1026 1027 qdf_op_unprotect(op_sync); 1028 1029 return err_size; 1030 } 1031 1032 int pktlogmod_init(void *context) 1033 { 1034 int ret; 1035 1036 /* create the proc directory entry */ 1037 g_pktlog_pde = proc_mkdir(PKTLOG_PROC_DIR, NULL); 1038 1039 if (!g_pktlog_pde) { 1040 qdf_nofl_info(PKTLOG_TAG "%s: proc_mkdir failed", __func__); 1041 return -EPERM; 1042 } 1043 1044 /* Attach packet log */ 1045 ret = pktlog_attach((struct hif_opaque_softc *)context); 1046 1047 /* If packet log init failed */ 1048 if (ret) 1049 goto attach_fail; 1050 1051 return ret; 1052 1053 attach_fail: 1054 remove_proc_entry(PKTLOG_PROC_DIR, NULL); 1055 g_pktlog_pde = NULL; 1056 1057 return ret; 1058 } 1059 1060 void pktlogmod_exit(void *context) 1061 { 1062 if (!g_pktlog_pde) 1063 return; 1064 1065 pktlog_detach((struct hif_opaque_softc *)context); 1066 1067 /* 1068 * pdev kill needs to be implemented 1069 */ 1070 remove_proc_entry(PKTLOG_PROC_DIR, NULL); 1071 } 1072 #endif 1073