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