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