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