xref: /wlan-dirver/qca-wifi-host-cmn/hif/src/ath_procfs.c (revision 8cfe6b10058a04cafb17eed051f2ddf11bee8931)
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 
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 
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_QCA5018) ||
122 	     (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
123 	     (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
124 	     (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
125 	     (tgt_info->target_type == TARGET_TYPE_KIWI) ||
126 	     (tgt_info->target_type == TARGET_TYPE_MANGO) ||
127 	     (tgt_info->target_type == TARGET_TYPE_PEACH))) ||
128 	    (scn->bus_type ==  QDF_BUS_TYPE_IPCI &&
129 	     (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
130 	    ((scn->bus_type ==  QDF_BUS_TYPE_USB) &&
131 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
132 		memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
133 		offset = (uint32_t)(*pos) & 0xffffff;
134 		hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
135 			 offset, memtype, count);
136 		rv = pld_athdiag_read(scn->qdf_dev->dev,
137 				      offset, memtype, count,
138 				      (uint8_t *)read_buffer);
139 		goto out;
140 	}
141 
142 	if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
143 		/* reading a word? */
144 		rv = hif_diag_read_access(hif_hdl, (uint32_t)(*pos),
145 					  (uint32_t *)read_buffer);
146 	} else {
147 		rv = hif_diag_read_mem(hif_hdl, (uint32_t)(*pos),
148 				       (uint8_t *)read_buffer, count);
149 	}
150 
151 out:
152 	if (rv) {
153 		qdf_mem_free(read_buffer);
154 		return -EIO;
155 	}
156 
157 	if (copy_to_user(buf, read_buffer, count)) {
158 		qdf_mem_free(read_buffer);
159 		hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
160 		return -EFAULT;
161 	}
162 	qdf_mem_free(read_buffer);
163 	return count;
164 }
165 
166 static ssize_t ath_procfs_diag_write_legacy(struct file *file,
167 					    const char __user *buf,
168 					    size_t count, loff_t *pos)
169 {
170 	hif_handle_t hif_hdl;
171 	int rv;
172 	uint8_t *write_buffer = NULL;
173 	struct hif_softc *scn;
174 	uint32_t offset = 0, memtype = 0;
175 	struct hif_target_info *tgt_info;
176 
177 	hif_hdl = get_hif_hdl_from_file(file);
178 	scn = HIF_GET_SOFTC(hif_hdl);
179 
180 	write_buffer = qdf_mem_malloc(count);
181 	if (!write_buffer)
182 		return -ENOMEM;
183 
184 	if (copy_from_user(write_buffer, buf, count)) {
185 		qdf_mem_free(write_buffer);
186 		hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
187 		return -EFAULT;
188 	}
189 
190 	hif_debug("wr buff 0x%pK buf 0x%pK cnt %zu offset 0x%x value 0x%x",
191 		 write_buffer, buf, count,
192 		 (int)*pos, *((uint32_t *) write_buffer));
193 
194 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
195 	if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
196 	    ((scn->bus_type ==  QDF_BUS_TYPE_PCI) &&
197 	     ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
198 	      (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
199 	      (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
200 	      (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
201 	      (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
202 	      (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
203 	      (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
204 	      (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
205 	      (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
206 	      (tgt_info->target_type == TARGET_TYPE_QCN9160) ||
207 	      (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
208 	      (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
209 	      (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
210 	      (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
211 	      (tgt_info->target_type == TARGET_TYPE_KIWI) ||
212 	      (tgt_info->target_type == TARGET_TYPE_MANGO) ||
213 	      (tgt_info->target_type == TARGET_TYPE_PEACH))) ||
214 	    (scn->bus_type ==  QDF_BUS_TYPE_IPCI &&
215 	     (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
216 	    ((scn->bus_type ==  QDF_BUS_TYPE_USB) &&
217 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
218 		memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
219 		offset = (uint32_t)(*pos) & 0xffffff;
220 		hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
221 			 offset, memtype, count);
222 		rv = pld_athdiag_write(scn->qdf_dev->dev,
223 				      offset, memtype, count,
224 				      (uint8_t *)write_buffer);
225 		goto out;
226 	}
227 
228 	if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
229 		/* reading a word? */
230 		uint32_t value = *((uint32_t *)write_buffer);
231 
232 		rv = hif_diag_write_access(hif_hdl, (uint32_t)(*pos), value);
233 	} else {
234 		rv = hif_diag_write_mem(hif_hdl, (uint32_t)(*pos),
235 					(uint8_t *)write_buffer, count);
236 	}
237 
238 out:
239 
240 	qdf_mem_free(write_buffer);
241 	if (rv == 0)
242 		return count;
243 	else
244 		return -EIO;
245 }
246 
247 #ifdef ATH_DIAG_EXT_DIRECT
248 /* Used to dump register or SRAM from target directly */
249 static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
250 				  uint8_t *buf, size_t count)
251 {
252 	size_t remaining = count;
253 	uint32_t *p_val = (uint32_t *)buf;
254 	uint32_t val;
255 	uint8_t *buf_d, *buf_s;
256 
257 	if (!scn->bus_ops.hif_reg_read32)
258 		return -EIO;
259 
260 	while (remaining >= 4) {
261 		*p_val++ = scn->bus_ops.hif_reg_read32(scn,
262 						       offset);
263 		offset += 4;
264 		remaining -= 4;
265 	}
266 
267 	if (remaining) {
268 		val = scn->bus_ops.hif_reg_read32(scn,
269 						  offset);
270 		buf_d = (uint8_t *)p_val;
271 		buf_s = (uint8_t *)&val;
272 		while (remaining) {
273 			*buf_d++ = *buf_s++;
274 			remaining--;
275 		}
276 	}
277 
278 	return 0;
279 }
280 
281 /* Used to write register or SRAM to target directly */
282 static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
283 				   uint8_t *buf, size_t count)
284 {
285 	size_t remaining = count;
286 	uint32_t *p_val = (uint32_t *)buf;
287 	uint32_t val;
288 	uint8_t *buf_d, *buf_s;
289 
290 	if (!scn->bus_ops.hif_reg_write32 || !scn->bus_ops.hif_reg_read32)
291 		return -EIO;
292 
293 	while (remaining >= 4) {
294 		scn->bus_ops.hif_reg_write32(scn,
295 					     offset,
296 					     *p_val++);
297 		offset += 4;
298 		remaining -= 4;
299 	}
300 
301 	if (remaining) {
302 		val = scn->bus_ops.hif_reg_read32(scn,
303 						  offset);
304 		buf_s = (uint8_t *)p_val;
305 		buf_d = (uint8_t *)&val;
306 		while (remaining) {
307 			*buf_d++ = *buf_s++;
308 			remaining--;
309 		}
310 		scn->bus_ops.hif_reg_write32(scn,
311 					     offset,
312 					     val);
313 	}
314 
315 	return 0;
316 }
317 #else
318 static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
319 				  uint8_t *buf, size_t count)
320 {
321 	return -EIO;
322 }
323 
324 /* Used to write register or SRAM to target directly */
325 static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
326 				   uint8_t *buf, size_t count)
327 {
328 	return -EIO;
329 }
330 
331 #endif
332 
333 static ssize_t ath_procfs_diag_read_ext(struct file *file, char __user *buf,
334 					size_t count,
335 					uint32_t op_type,
336 					uint32_t memtype,
337 					uint32_t offset)
338 {
339 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
340 	int rv = -EINVAL;
341 	uint8_t *read_buffer;
342 	struct hif_softc *scn;
343 	struct hif_target_info *tgt_info;
344 
345 	if (!hif_hdl)
346 		return -EINVAL;
347 
348 	read_buffer = qdf_mem_malloc(count);
349 	if (!read_buffer)
350 		return -ENOMEM;
351 
352 	scn = HIF_GET_SOFTC(hif_hdl);
353 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
354 	switch (scn->bus_type) {
355 	case QDF_BUS_TYPE_PCI:
356 		switch (tgt_info->target_type) {
357 		case TARGET_TYPE_QCA6390:
358 		case TARGET_TYPE_QCA6490:
359 		case TARGET_TYPE_KIWI:
360 		case TARGET_TYPE_PEACH:
361 		case TARGET_TYPE_MANGO:
362 			if (op_type == OP_TYPE_EXT_DIRECT)
363 				rv = ath_procfs_direct_read(scn,
364 							    offset,
365 							    read_buffer,
366 							    count);
367 			else
368 				rv = pld_athdiag_read(scn->qdf_dev->dev,
369 						      offset,
370 						      memtype,
371 						      count,
372 						      read_buffer);
373 			break;
374 		default:
375 			hif_err("Unrecognized target type %d",
376 				tgt_info->target_type);
377 		}
378 		break;
379 	default:
380 		hif_err("Unrecognized bus type %d", scn->bus_type);
381 		break;
382 	}
383 
384 	if (rv) {
385 		hif_err("fail to read from target %d", rv);
386 	} else {
387 		rv = count;
388 		if (copy_to_user(buf, read_buffer, count)) {
389 			hif_err("copy_to_user error in /proc/%s",
390 				PROCFS_NAME);
391 			rv = -EFAULT;
392 		}
393 	}
394 
395 	qdf_mem_free(read_buffer);
396 
397 	return rv;
398 }
399 
400 static ssize_t ath_procfs_diag_write_ext(struct file *file,
401 					 const char __user *buf,
402 					 size_t count,
403 					 uint32_t op_type,
404 					 uint32_t memtype,
405 					 uint32_t offset)
406 {
407 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
408 	int rv = -EINVAL;
409 	uint8_t *write_buffer;
410 	struct hif_softc *scn;
411 	struct hif_target_info *tgt_info;
412 
413 	if (!hif_hdl)
414 		return -EINVAL;
415 
416 	scn = HIF_GET_SOFTC(hif_hdl);
417 
418 	write_buffer = qdf_mem_malloc(count);
419 	if (!write_buffer)
420 		return -ENOMEM;
421 
422 	if (copy_from_user(write_buffer, buf, count)) {
423 		qdf_mem_free(write_buffer);
424 		hif_err("copy_to_user error in /proc/%s",
425 			PROCFS_NAME);
426 		return -EFAULT;
427 	}
428 
429 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
430 
431 	switch (scn->bus_type) {
432 	case QDF_BUS_TYPE_PCI:
433 		switch (tgt_info->target_type) {
434 		case TARGET_TYPE_QCA6390:
435 		case TARGET_TYPE_QCA6490:
436 		case TARGET_TYPE_KIWI:
437 		case TARGET_TYPE_MANGO:
438 		case TARGET_TYPE_PEACH:
439 			if (op_type == OP_TYPE_EXT_DIRECT)
440 				rv = ath_procfs_direct_write(scn,
441 							     offset,
442 							     write_buffer,
443 							     count);
444 			else
445 				rv = pld_athdiag_write(scn->qdf_dev->dev,
446 						       offset,
447 						       memtype,
448 						       count,
449 						       write_buffer);
450 			break;
451 		default:
452 			hif_err("Unrecognized target type %d",
453 				tgt_info->target_type);
454 		}
455 		break;
456 	default:
457 		hif_err("Unrecognized bus type %d", scn->bus_type);
458 		break;
459 	}
460 
461 	qdf_mem_free(write_buffer);
462 
463 	return (rv == 0) ? count : -EIO;
464 }
465 
466 static void get_fields_from_pos(loff_t pos,
467 				uint32_t *op_type,
468 				uint32_t *memtype,
469 				uint32_t *offset)
470 {
471 	*op_type = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OP_TYPE_INDEX,
472 				  ATH_DIAG_EXT_OP_TYPE_BITS);
473 	*memtype = QDF_GET_BITS64(pos, ATH_DIAG_EXT_MEM_TYPE_INDEX,
474 				  ATH_DIAG_EXT_MEM_TYPE_BITS);
475 	*offset = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OFFSET_INDEX,
476 				 ATH_DIAG_EXT_OFFSET_BITS);
477 }
478 
479 static ssize_t ath_procfs_diag_read(struct file *file, char __user *buf,
480 				    size_t count, loff_t *pos)
481 {
482 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
483 	int rv = -EINVAL;
484 	struct hif_softc *scn;
485 	uint32_t offset, memtype;
486 	uint32_t op_type;
487 
488 	if (!hif_hdl)
489 		return -EINVAL;
490 
491 	get_fields_from_pos(*pos, &op_type, &memtype, &offset);
492 
493 	scn = HIF_GET_SOFTC(hif_hdl);
494 	if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
495 		return -EINVAL;
496 
497 	if (offset & 0x3)
498 		return -EINVAL;
499 
500 	hif_info("rd cnt %zu offset 0x%x op_type %d type %d pos %llx",
501 		 count, offset, op_type, memtype, *pos);
502 
503 	switch (op_type) {
504 	case OP_TYPE_LEGACY:
505 		rv = ath_procfs_diag_read_legacy(file, buf, count, pos);
506 		break;
507 	case OP_TYPE_EXT_QMI:
508 	case OP_TYPE_EXT_DIRECT:
509 		rv = ath_procfs_diag_read_ext(file, buf, count, op_type,
510 					      memtype, offset);
511 		break;
512 	default:
513 		hif_err("Unrecognized op type %d", op_type);
514 		break;
515 	}
516 
517 	return rv;
518 }
519 
520 static ssize_t ath_procfs_diag_write(struct file *file,
521 				     const char __user *buf,
522 				     size_t count, loff_t *pos)
523 {
524 	hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
525 	int rv = -EINVAL;
526 	struct hif_softc *scn;
527 	uint32_t offset, memtype;
528 	uint32_t op_type;
529 
530 	if (!hif_hdl)
531 		return -EINVAL;
532 
533 	get_fields_from_pos(*pos, &op_type, &memtype, &offset);
534 
535 	scn = HIF_GET_SOFTC(hif_hdl);
536 	if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
537 		return -EINVAL;
538 
539 	if (offset & 0x3)
540 		return -EINVAL;
541 
542 	hif_info("wr cnt %zu offset 0x%x op_type %d mem_type %d",
543 		 count, offset, op_type, memtype);
544 
545 	switch (op_type) {
546 	case OP_TYPE_LEGACY:
547 		rv = ath_procfs_diag_write_legacy(file, buf, count, pos);
548 		break;
549 	case OP_TYPE_EXT_QMI:
550 	case OP_TYPE_EXT_DIRECT:
551 		rv = ath_procfs_diag_write_ext(file, buf, count, op_type,
552 					       memtype, offset);
553 		break;
554 	default:
555 		hif_err("Unrecognized op type %d", op_type);
556 		break;
557 	}
558 
559 	return rv;
560 }
561 
562 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
563 static const struct proc_ops athdiag_fops = {
564 	.proc_read = ath_procfs_diag_read,
565 	.proc_write = ath_procfs_diag_write,
566 	.proc_lseek = default_llseek,
567 };
568 #else
569 static const struct file_operations athdiag_fops = {
570 	.read = ath_procfs_diag_read,
571 	.write = ath_procfs_diag_write,
572 };
573 #endif
574 
575 /*
576  * This function is called when the module is loaded
577  *
578  */
579 int athdiag_procfs_init(void *scn)
580 {
581 	proc_dir = proc_mkdir(PROCFS_DIR, NULL);
582 	if (!proc_dir) {
583 		remove_proc_entry(PROCFS_DIR, NULL);
584 		hif_err("Could not initialize /proc/%s", PROCFS_DIR);
585 		return -ENOMEM;
586 	}
587 
588 	proc_file = proc_create_data(PROCFS_NAME, 0600, proc_dir,
589 				     &athdiag_fops, (void *)scn);
590 	if (!proc_file) {
591 		remove_proc_entry(PROCFS_NAME, proc_dir);
592 		hif_err("Could not initialize /proc/%s", PROCFS_NAME);
593 		return -ENOMEM;
594 	}
595 
596 	hif_debug("/proc/%s/%s created", PROCFS_DIR, PROCFS_NAME);
597 	return 0;
598 }
599 
600 /*
601  * This function is called when the module is unloaded
602  *
603  */
604 void athdiag_procfs_remove(void)
605 {
606 	if (proc_dir) {
607 		remove_proc_entry(PROCFS_NAME, proc_dir);
608 		hif_debug("/proc/%s/%s removed", PROCFS_DIR, PROCFS_NAME);
609 		remove_proc_entry(PROCFS_DIR, NULL);
610 		hif_debug("/proc/%s removed", PROCFS_DIR);
611 		proc_dir = NULL;
612 	}
613 }
614 #else
615 int athdiag_procfs_init(void *scn)
616 {
617 	return 0;
618 }
619 void athdiag_procfs_remove(void) {}
620 #endif
621