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