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