xref: /wlan-dirver/qca-wifi-host-cmn/utils/pktlog/linux_ac.c (revision 8b3dca18206e1a0461492f082fa6e270b092c035)
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