1 /*
2 * Copyright (c) 2013-2014, 2016-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021-2023 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 #if defined(CONFIG_ATH_PROCFS_DIAG_SUPPORT)
21 #include <linux/module.h> /* Specifically, a module */
22 #include <linux/kernel.h> /* We're doing kernel work */
23 #include <linux/version.h> /* We're doing kernel work */
24 #include <linux/proc_fs.h> /* Necessary because we use the proc fs */
25 #include <linux/uaccess.h> /* for copy_from_user */
26 #include "hif.h"
27 #include "hif_main.h"
28 #if defined(HIF_USB)
29 #include "if_usb.h"
30 #endif
31 #if defined(HIF_SDIO)
32 #include "if_sdio.h"
33 #endif
34 #include "hif_debug.h"
35 #include "pld_common.h"
36 #include "target_type.h"
37
38 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0))
39 /*
40 * Commit 359745d78351 ("proc: remove PDE_DATA() completely")
41 * Replaced PDE_DATA() with pde_data()
42 */
43 #define pde_data(inode) PDE_DATA(inode)
44 #endif
45
46 #define PROCFS_NAME "athdiagpfs"
47 #ifdef MULTI_IF_NAME
48 #define PROCFS_DIR "cld" MULTI_IF_NAME
49 #else
50 #define PROCFS_DIR "cld"
51 #endif
52
53 /*
54 * Get op_type, mem_type and offset fields from pos of procfs
55 * It will reuse pos, which is long long type
56 *
57 * op_type: 4 bits
58 * memtype: 8 bits
59 * reserve1: 20 bits
60 * offset: 32 bits
61 */
62 #define OP_TYPE_LEGACY 0
63 #define OP_TYPE_EXT_QMI 1
64 #define OP_TYPE_EXT_DIRECT 2
65
66 #define ATH_DIAG_EXT_OP_TYPE_BITS 4
67 #define ATH_DIAG_EXT_OP_TYPE_INDEX 60
68 #define ATH_DIAG_EXT_MEM_TYPE_BITS 8
69 #define ATH_DIAG_EXT_MEM_TYPE_INDEX 52
70 #define ATH_DIAG_EXT_OFFSET_BITS 32
71 #define ATH_DIAG_EXT_OFFSET_INDEX 0
72
73 /*
74 * This structure hold information about the /proc file
75 *
76 */
77 static struct proc_dir_entry *proc_file, *proc_dir;
78
get_hif_hdl_from_file(struct file * file)79 static void *get_hif_hdl_from_file(struct file *file)
80 {
81 struct hif_opaque_softc *scn;
82
83 scn = (struct hif_opaque_softc *)pde_data(file_inode(file));
84 return (void *)scn;
85 }
86
ath_procfs_diag_read_legacy(struct file * file,char __user * buf,size_t count,loff_t * pos)87 static ssize_t ath_procfs_diag_read_legacy(struct file *file,
88 char __user *buf,
89 size_t count, loff_t *pos)
90 {
91 hif_handle_t hif_hdl;
92 int rv;
93 uint8_t *read_buffer = NULL;
94 struct hif_softc *scn;
95 uint32_t offset = 0, memtype = 0;
96 struct hif_target_info *tgt_info;
97
98 hif_hdl = get_hif_hdl_from_file(file);
99 scn = HIF_GET_SOFTC(hif_hdl);
100
101 read_buffer = qdf_mem_malloc(count);
102 if (!read_buffer)
103 return -ENOMEM;
104
105 hif_debug("rd buff 0x%pK cnt %zu offset 0x%x buf 0x%pK",
106 read_buffer, count, (int)*pos, buf);
107
108 tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
109 if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
110 (scn->bus_type == QDF_BUS_TYPE_PCI &&
111 ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
112 (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
113 (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
114 (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
115 (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
116 (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
117 (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
118 (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
119 (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
120 (tgt_info->target_type == TARGET_TYPE_QCN9160) ||
121 (tgt_info->target_type == TARGET_TYPE_QCN6432) ||
122 (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
123 (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
124 (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
125 (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
126 (tgt_info->target_type == TARGET_TYPE_KIWI) ||
127 (tgt_info->target_type == TARGET_TYPE_MANGO) ||
128 (tgt_info->target_type == TARGET_TYPE_PEACH))) ||
129 (scn->bus_type == QDF_BUS_TYPE_IPCI &&
130 (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
131 ((scn->bus_type == QDF_BUS_TYPE_USB) &&
132 (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
133 memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
134 offset = (uint32_t)(*pos) & 0xffffff;
135 hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
136 offset, memtype, count);
137 rv = pld_athdiag_read(scn->qdf_dev->dev,
138 offset, memtype, count,
139 (uint8_t *)read_buffer);
140 goto out;
141 }
142
143 if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
144 /* reading a word? */
145 rv = hif_diag_read_access(hif_hdl, (uint32_t)(*pos),
146 (uint32_t *)read_buffer);
147 } else {
148 rv = hif_diag_read_mem(hif_hdl, (uint32_t)(*pos),
149 (uint8_t *)read_buffer, count);
150 }
151
152 out:
153 if (rv) {
154 qdf_mem_free(read_buffer);
155 return -EIO;
156 }
157
158 if (copy_to_user(buf, read_buffer, count)) {
159 qdf_mem_free(read_buffer);
160 hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
161 return -EFAULT;
162 }
163 qdf_mem_free(read_buffer);
164 return count;
165 }
166
ath_procfs_diag_write_legacy(struct file * file,const char __user * buf,size_t count,loff_t * pos)167 static ssize_t ath_procfs_diag_write_legacy(struct file *file,
168 const char __user *buf,
169 size_t count, loff_t *pos)
170 {
171 hif_handle_t hif_hdl;
172 int rv;
173 uint8_t *write_buffer = NULL;
174 struct hif_softc *scn;
175 uint32_t offset = 0, memtype = 0;
176 struct hif_target_info *tgt_info;
177
178 hif_hdl = get_hif_hdl_from_file(file);
179 scn = HIF_GET_SOFTC(hif_hdl);
180
181 write_buffer = qdf_mem_malloc(count);
182 if (!write_buffer)
183 return -ENOMEM;
184
185 if (copy_from_user(write_buffer, buf, count)) {
186 qdf_mem_free(write_buffer);
187 hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
188 return -EFAULT;
189 }
190
191 hif_debug("wr buff 0x%pK buf 0x%pK cnt %zu offset 0x%x value 0x%x",
192 write_buffer, buf, count,
193 (int)*pos, *((uint32_t *) write_buffer));
194
195 tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
196 if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
197 ((scn->bus_type == QDF_BUS_TYPE_PCI) &&
198 ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
199 (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
200 (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
201 (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
202 (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
203 (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
204 (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
205 (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
206 (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
207 (tgt_info->target_type == TARGET_TYPE_QCN9160) ||
208 (tgt_info->target_type == TARGET_TYPE_QCN6432) ||
209 (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
210 (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
211 (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
212 (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
213 (tgt_info->target_type == TARGET_TYPE_KIWI) ||
214 (tgt_info->target_type == TARGET_TYPE_MANGO) ||
215 (tgt_info->target_type == TARGET_TYPE_PEACH))) ||
216 (scn->bus_type == QDF_BUS_TYPE_IPCI &&
217 (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
218 ((scn->bus_type == QDF_BUS_TYPE_USB) &&
219 (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
220 memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
221 offset = (uint32_t)(*pos) & 0xffffff;
222 hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
223 offset, memtype, count);
224 rv = pld_athdiag_write(scn->qdf_dev->dev,
225 offset, memtype, count,
226 (uint8_t *)write_buffer);
227 goto out;
228 }
229
230 if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
231 /* reading a word? */
232 uint32_t value = *((uint32_t *)write_buffer);
233
234 rv = hif_diag_write_access(hif_hdl, (uint32_t)(*pos), value);
235 } else {
236 rv = hif_diag_write_mem(hif_hdl, (uint32_t)(*pos),
237 (uint8_t *)write_buffer, count);
238 }
239
240 out:
241
242 qdf_mem_free(write_buffer);
243 if (rv == 0)
244 return count;
245 else
246 return -EIO;
247 }
248
249 #ifdef ATH_DIAG_EXT_DIRECT
250 /* Used to dump register or SRAM from target directly */
ath_procfs_direct_read(struct hif_softc * scn,uint32_t offset,uint8_t * buf,size_t count)251 static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
252 uint8_t *buf, size_t count)
253 {
254 size_t remaining = count;
255 uint32_t *p_val = (uint32_t *)buf;
256 uint32_t val;
257 uint8_t *buf_d, *buf_s;
258
259 if (!scn->bus_ops.hif_reg_read32)
260 return -EIO;
261
262 while (remaining >= 4) {
263 *p_val++ = scn->bus_ops.hif_reg_read32(scn,
264 offset);
265 offset += 4;
266 remaining -= 4;
267 }
268
269 if (remaining) {
270 val = scn->bus_ops.hif_reg_read32(scn,
271 offset);
272 buf_d = (uint8_t *)p_val;
273 buf_s = (uint8_t *)&val;
274 while (remaining) {
275 *buf_d++ = *buf_s++;
276 remaining--;
277 }
278 }
279
280 return 0;
281 }
282
283 /* Used to write register or SRAM to target directly */
ath_procfs_direct_write(struct hif_softc * scn,uint32_t offset,uint8_t * buf,size_t count)284 static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
285 uint8_t *buf, size_t count)
286 {
287 size_t remaining = count;
288 uint32_t *p_val = (uint32_t *)buf;
289 uint32_t val;
290 uint8_t *buf_d, *buf_s;
291
292 if (!scn->bus_ops.hif_reg_write32 || !scn->bus_ops.hif_reg_read32)
293 return -EIO;
294
295 while (remaining >= 4) {
296 scn->bus_ops.hif_reg_write32(scn,
297 offset,
298 *p_val++);
299 offset += 4;
300 remaining -= 4;
301 }
302
303 if (remaining) {
304 val = scn->bus_ops.hif_reg_read32(scn,
305 offset);
306 buf_s = (uint8_t *)p_val;
307 buf_d = (uint8_t *)&val;
308 while (remaining) {
309 *buf_d++ = *buf_s++;
310 remaining--;
311 }
312 scn->bus_ops.hif_reg_write32(scn,
313 offset,
314 val);
315 }
316
317 return 0;
318 }
319 #else
ath_procfs_direct_read(struct hif_softc * scn,uint32_t offset,uint8_t * buf,size_t count)320 static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
321 uint8_t *buf, size_t count)
322 {
323 return -EIO;
324 }
325
326 /* Used to write register or SRAM to target directly */
ath_procfs_direct_write(struct hif_softc * scn,uint32_t offset,uint8_t * buf,size_t count)327 static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
328 uint8_t *buf, size_t count)
329 {
330 return -EIO;
331 }
332
333 #endif
334
ath_procfs_diag_read_ext(struct file * file,char __user * buf,size_t count,uint32_t op_type,uint32_t memtype,uint32_t offset)335 static ssize_t ath_procfs_diag_read_ext(struct file *file, char __user *buf,
336 size_t count,
337 uint32_t op_type,
338 uint32_t memtype,
339 uint32_t offset)
340 {
341 hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
342 int rv = -EINVAL;
343 uint8_t *read_buffer;
344 struct hif_softc *scn;
345 struct hif_target_info *tgt_info;
346
347 if (!hif_hdl)
348 return -EINVAL;
349
350 read_buffer = qdf_mem_malloc(count);
351 if (!read_buffer)
352 return -ENOMEM;
353
354 scn = HIF_GET_SOFTC(hif_hdl);
355 tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
356 switch (scn->bus_type) {
357 case QDF_BUS_TYPE_PCI:
358 switch (tgt_info->target_type) {
359 case TARGET_TYPE_QCA6390:
360 case TARGET_TYPE_QCA6490:
361 case TARGET_TYPE_KIWI:
362 case TARGET_TYPE_PEACH:
363 case TARGET_TYPE_MANGO:
364 if (op_type == OP_TYPE_EXT_DIRECT)
365 rv = ath_procfs_direct_read(scn,
366 offset,
367 read_buffer,
368 count);
369 else
370 rv = pld_athdiag_read(scn->qdf_dev->dev,
371 offset,
372 memtype,
373 count,
374 read_buffer);
375 break;
376 default:
377 hif_err("Unrecognized target type %d",
378 tgt_info->target_type);
379 }
380 break;
381 default:
382 hif_err("Unrecognized bus type %d", scn->bus_type);
383 break;
384 }
385
386 if (rv) {
387 hif_err("fail to read from target %d", rv);
388 } else {
389 rv = count;
390 if (copy_to_user(buf, read_buffer, count)) {
391 hif_err("copy_to_user error in /proc/%s",
392 PROCFS_NAME);
393 rv = -EFAULT;
394 }
395 }
396
397 qdf_mem_free(read_buffer);
398
399 return rv;
400 }
401
ath_procfs_diag_write_ext(struct file * file,const char __user * buf,size_t count,uint32_t op_type,uint32_t memtype,uint32_t offset)402 static ssize_t ath_procfs_diag_write_ext(struct file *file,
403 const char __user *buf,
404 size_t count,
405 uint32_t op_type,
406 uint32_t memtype,
407 uint32_t offset)
408 {
409 hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
410 int rv = -EINVAL;
411 uint8_t *write_buffer;
412 struct hif_softc *scn;
413 struct hif_target_info *tgt_info;
414
415 if (!hif_hdl)
416 return -EINVAL;
417
418 scn = HIF_GET_SOFTC(hif_hdl);
419
420 write_buffer = qdf_mem_malloc(count);
421 if (!write_buffer)
422 return -ENOMEM;
423
424 if (copy_from_user(write_buffer, buf, count)) {
425 qdf_mem_free(write_buffer);
426 hif_err("copy_to_user error in /proc/%s",
427 PROCFS_NAME);
428 return -EFAULT;
429 }
430
431 tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
432
433 switch (scn->bus_type) {
434 case QDF_BUS_TYPE_PCI:
435 switch (tgt_info->target_type) {
436 case TARGET_TYPE_QCA6390:
437 case TARGET_TYPE_QCA6490:
438 case TARGET_TYPE_KIWI:
439 case TARGET_TYPE_MANGO:
440 case TARGET_TYPE_PEACH:
441 if (op_type == OP_TYPE_EXT_DIRECT)
442 rv = ath_procfs_direct_write(scn,
443 offset,
444 write_buffer,
445 count);
446 else
447 rv = pld_athdiag_write(scn->qdf_dev->dev,
448 offset,
449 memtype,
450 count,
451 write_buffer);
452 break;
453 default:
454 hif_err("Unrecognized target type %d",
455 tgt_info->target_type);
456 }
457 break;
458 default:
459 hif_err("Unrecognized bus type %d", scn->bus_type);
460 break;
461 }
462
463 qdf_mem_free(write_buffer);
464
465 return (rv == 0) ? count : -EIO;
466 }
467
get_fields_from_pos(loff_t pos,uint32_t * op_type,uint32_t * memtype,uint32_t * offset)468 static void get_fields_from_pos(loff_t pos,
469 uint32_t *op_type,
470 uint32_t *memtype,
471 uint32_t *offset)
472 {
473 *op_type = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OP_TYPE_INDEX,
474 ATH_DIAG_EXT_OP_TYPE_BITS);
475 *memtype = QDF_GET_BITS64(pos, ATH_DIAG_EXT_MEM_TYPE_INDEX,
476 ATH_DIAG_EXT_MEM_TYPE_BITS);
477 *offset = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OFFSET_INDEX,
478 ATH_DIAG_EXT_OFFSET_BITS);
479 }
480
ath_procfs_diag_read(struct file * file,char __user * buf,size_t count,loff_t * pos)481 static ssize_t ath_procfs_diag_read(struct file *file, char __user *buf,
482 size_t count, loff_t *pos)
483 {
484 hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
485 int rv = -EINVAL;
486 struct hif_softc *scn;
487 uint32_t offset, memtype;
488 uint32_t op_type;
489
490 if (!hif_hdl)
491 return -EINVAL;
492
493 get_fields_from_pos(*pos, &op_type, &memtype, &offset);
494
495 scn = HIF_GET_SOFTC(hif_hdl);
496 if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
497 return -EINVAL;
498
499 if (offset & 0x3)
500 return -EINVAL;
501
502 hif_debug("rd cnt %zu offset 0x%x op_type %d type %d pos %llx",
503 count, offset, op_type, memtype, *pos);
504
505 switch (op_type) {
506 case OP_TYPE_LEGACY:
507 rv = ath_procfs_diag_read_legacy(file, buf, count, pos);
508 break;
509 case OP_TYPE_EXT_QMI:
510 case OP_TYPE_EXT_DIRECT:
511 rv = ath_procfs_diag_read_ext(file, buf, count, op_type,
512 memtype, offset);
513 break;
514 default:
515 hif_err("Unrecognized op type %d", op_type);
516 break;
517 }
518
519 return rv;
520 }
521
ath_procfs_diag_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)522 static ssize_t ath_procfs_diag_write(struct file *file,
523 const char __user *buf,
524 size_t count, loff_t *pos)
525 {
526 hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
527 int rv = -EINVAL;
528 struct hif_softc *scn;
529 uint32_t offset, memtype;
530 uint32_t op_type;
531
532 if (!hif_hdl)
533 return -EINVAL;
534
535 get_fields_from_pos(*pos, &op_type, &memtype, &offset);
536
537 scn = HIF_GET_SOFTC(hif_hdl);
538 if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
539 return -EINVAL;
540
541 if (offset & 0x3)
542 return -EINVAL;
543
544 hif_debug("wr cnt %zu offset 0x%x op_type %d mem_type %d",
545 count, offset, op_type, memtype);
546
547 switch (op_type) {
548 case OP_TYPE_LEGACY:
549 rv = ath_procfs_diag_write_legacy(file, buf, count, pos);
550 break;
551 case OP_TYPE_EXT_QMI:
552 case OP_TYPE_EXT_DIRECT:
553 rv = ath_procfs_diag_write_ext(file, buf, count, op_type,
554 memtype, offset);
555 break;
556 default:
557 hif_err("Unrecognized op type %d", op_type);
558 break;
559 }
560
561 return rv;
562 }
563
564 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
565 static const struct proc_ops athdiag_fops = {
566 .proc_read = ath_procfs_diag_read,
567 .proc_write = ath_procfs_diag_write,
568 .proc_lseek = default_llseek,
569 };
570 #else
571 static const struct file_operations athdiag_fops = {
572 .read = ath_procfs_diag_read,
573 .write = ath_procfs_diag_write,
574 };
575 #endif
576
577 /*
578 * This function is called when the module is loaded
579 *
580 */
athdiag_procfs_init(void * scn)581 int athdiag_procfs_init(void *scn)
582 {
583 proc_dir = proc_mkdir(PROCFS_DIR, NULL);
584 if (!proc_dir) {
585 remove_proc_entry(PROCFS_DIR, NULL);
586 hif_err("Could not initialize /proc/%s", PROCFS_DIR);
587 return -ENOMEM;
588 }
589
590 proc_file = proc_create_data(PROCFS_NAME, 0600, proc_dir,
591 &athdiag_fops, (void *)scn);
592 if (!proc_file) {
593 remove_proc_entry(PROCFS_NAME, proc_dir);
594 hif_err("Could not initialize /proc/%s", PROCFS_NAME);
595 return -ENOMEM;
596 }
597
598 hif_debug("/proc/%s/%s created", PROCFS_DIR, PROCFS_NAME);
599 return 0;
600 }
601
602 /*
603 * This function is called when the module is unloaded
604 *
605 */
athdiag_procfs_remove(void)606 void athdiag_procfs_remove(void)
607 {
608 if (proc_dir) {
609 remove_proc_entry(PROCFS_NAME, proc_dir);
610 hif_debug("/proc/%s/%s removed", PROCFS_DIR, PROCFS_NAME);
611 remove_proc_entry(PROCFS_DIR, NULL);
612 hif_debug("/proc/%s removed", PROCFS_DIR);
613 proc_dir = NULL;
614 }
615 }
616 #else
athdiag_procfs_init(void * scn)617 int athdiag_procfs_init(void *scn)
618 {
619 return 0;
620 }
athdiag_procfs_remove(void)621 void athdiag_procfs_remove(void) {}
622 #endif
623