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