xref: /wlan-dirver/qca-wifi-host-cmn/hif/src/ath_procfs.c (revision a86b23ee68a2491aede2e03991f3fb37046f4e41)
1 /*
2  * Copyright (c) 2013-2014, 2016-2020 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 #if defined(CONFIG_ATH_PROCFS_DIAG_SUPPORT)
20 #include <linux/module.h>       /* Specifically, a module */
21 #include <linux/kernel.h>       /* We're doing kernel work */
22 #include <linux/version.h>      /* We're doing kernel work */
23 #include <linux/proc_fs.h>      /* Necessary because we use the proc fs */
24 #include <linux/uaccess.h>        /* for copy_from_user */
25 #include "hif.h"
26 #include "hif_main.h"
27 #if defined(HIF_USB)
28 #include "if_usb.h"
29 #endif
30 #if defined(HIF_SDIO)
31 #include "if_sdio.h"
32 #endif
33 #include "hif_debug.h"
34 #include "pld_common.h"
35 #include "target_type.h"
36 
37 #define PROCFS_NAME             "athdiagpfs"
38 #ifdef MULTI_IF_NAME
39 #define PROCFS_DIR              "cld" MULTI_IF_NAME
40 #else
41 #define PROCFS_DIR              "cld"
42 #endif
43 
44 /**
45  * This structure hold information about the /proc file
46  *
47  */
48 static struct proc_dir_entry *proc_file, *proc_dir;
49 
50 static void *get_hif_hdl_from_file(struct file *file)
51 {
52 	struct hif_opaque_softc *scn;
53 
54 	scn = (struct hif_opaque_softc *)PDE_DATA(file_inode(file));
55 	return (void *)scn;
56 }
57 
58 static ssize_t ath_procfs_diag_read(struct file *file, char __user *buf,
59 				    size_t count, loff_t *pos)
60 {
61 	hif_handle_t hif_hdl;
62 	int rv;
63 	uint8_t *read_buffer = NULL;
64 	struct hif_softc *scn;
65 	uint32_t offset = 0, memtype = 0;
66 	struct hif_target_info *tgt_info;
67 
68 	hif_hdl = get_hif_hdl_from_file(file);
69 	scn = HIF_GET_SOFTC(hif_hdl);
70 
71 	if (scn->bus_ops.hif_addr_in_boundary(hif_hdl, (uint32_t)(*pos)))
72 		return -EINVAL;
73 
74 	read_buffer = qdf_mem_malloc(count);
75 	if (!read_buffer)
76 		return -ENOMEM;
77 
78 	HIF_DBG("rd buff 0x%pK cnt %zu offset 0x%x buf 0x%pK",
79 		 read_buffer, count, (int)*pos, buf);
80 
81 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
82 	if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
83 	    (scn->bus_type ==  QDF_BUS_TYPE_PCI &&
84 	    ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
85 	     (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
86 	     (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
87 	     (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
88 	     (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
89 	     (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
90 	     (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
91 	     (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
92 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) ||
93 	    (scn->bus_type ==  QDF_BUS_TYPE_IPCI &&
94 	     (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
95 	    ((scn->bus_type ==  QDF_BUS_TYPE_USB) &&
96 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
97 		memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
98 		offset = (uint32_t)(*pos) & 0xffffff;
99 		HIF_DBG("%s: offset 0x%x memtype 0x%x, datalen %zu\n",
100 			__func__, offset, memtype, count);
101 		rv = pld_athdiag_read(scn->qdf_dev->dev,
102 				      offset, memtype, count,
103 				      (uint8_t *)read_buffer);
104 		goto out;
105 	}
106 
107 	if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
108 		/* reading a word? */
109 		rv = hif_diag_read_access(hif_hdl, (uint32_t)(*pos),
110 					  (uint32_t *)read_buffer);
111 	} else {
112 		rv = hif_diag_read_mem(hif_hdl, (uint32_t)(*pos),
113 				       (uint8_t *)read_buffer, count);
114 	}
115 
116 out:
117 	if (rv) {
118 		qdf_mem_free(read_buffer);
119 		return -EIO;
120 	}
121 
122 	if (copy_to_user(buf, read_buffer, count)) {
123 		qdf_mem_free(read_buffer);
124 		HIF_ERROR("%s: copy_to_user error in /proc/%s",
125 			__func__, PROCFS_NAME);
126 		return -EFAULT;
127 	}
128 	qdf_mem_free(read_buffer);
129 	return count;
130 }
131 
132 static ssize_t ath_procfs_diag_write(struct file *file,
133 				     const char __user *buf,
134 				     size_t count, loff_t *pos)
135 {
136 	hif_handle_t hif_hdl;
137 	int rv;
138 	uint8_t *write_buffer = NULL;
139 	struct hif_softc *scn;
140 	uint32_t offset = 0, memtype = 0;
141 	struct hif_target_info *tgt_info;
142 
143 	hif_hdl = get_hif_hdl_from_file(file);
144 	scn = HIF_GET_SOFTC(hif_hdl);
145 
146 	if (scn->bus_ops.hif_addr_in_boundary(hif_hdl, (uint32_t)(*pos)))
147 		return -EINVAL;
148 
149 	write_buffer = qdf_mem_malloc(count);
150 	if (!write_buffer)
151 		return -ENOMEM;
152 
153 	if (copy_from_user(write_buffer, buf, count)) {
154 		qdf_mem_free(write_buffer);
155 		HIF_ERROR("%s: copy_to_user error in /proc/%s",
156 			__func__, PROCFS_NAME);
157 		return -EFAULT;
158 	}
159 
160 	HIF_DBG("wr buff 0x%pK buf 0x%pK cnt %zu offset 0x%x value 0x%x",
161 		 write_buffer, buf, count,
162 		 (int)*pos, *((uint32_t *) write_buffer));
163 
164 	tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
165 	if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
166 	    ((scn->bus_type ==  QDF_BUS_TYPE_PCI) &&
167 	     ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
168 	      (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
169 	      (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
170 	      (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
171 	      (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
172 	      (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
173 	      (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
174 	      (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
175 	      (tgt_info->target_type == TARGET_TYPE_QCN7605))) ||
176 	    (scn->bus_type ==  QDF_BUS_TYPE_IPCI &&
177 	     (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
178 	    ((scn->bus_type ==  QDF_BUS_TYPE_USB) &&
179 	     (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
180 		memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
181 		offset = (uint32_t)(*pos) & 0xffffff;
182 		HIF_DBG("%s: offset 0x%x memtype 0x%x, datalen %zu\n",
183 			__func__, offset, memtype, count);
184 		rv = pld_athdiag_write(scn->qdf_dev->dev,
185 				      offset, memtype, count,
186 				      (uint8_t *)write_buffer);
187 		goto out;
188 	}
189 
190 	if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
191 		/* reading a word? */
192 		uint32_t value = *((uint32_t *)write_buffer);
193 
194 		rv = hif_diag_write_access(hif_hdl, (uint32_t)(*pos), value);
195 	} else {
196 		rv = hif_diag_write_mem(hif_hdl, (uint32_t)(*pos),
197 					(uint8_t *)write_buffer, count);
198 	}
199 
200 out:
201 
202 	qdf_mem_free(write_buffer);
203 	if (rv == 0)
204 		return count;
205 	else
206 		return -EIO;
207 }
208 
209 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
210 static const struct proc_ops athdiag_fops = {
211 	.proc_read = ath_procfs_diag_read,
212 	.proc_write = ath_procfs_diag_write,
213 };
214 #else
215 static const struct file_operations athdiag_fops = {
216 	.read = ath_procfs_diag_read,
217 	.write = ath_procfs_diag_write,
218 };
219 #endif
220 
221 /*
222  * This function is called when the module is loaded
223  *
224  */
225 int athdiag_procfs_init(void *scn)
226 {
227 	proc_dir = proc_mkdir(PROCFS_DIR, NULL);
228 	if (!proc_dir) {
229 		remove_proc_entry(PROCFS_DIR, NULL);
230 		HIF_ERROR("%s: Error: Could not initialize /proc/%s",
231 			__func__, PROCFS_DIR);
232 		return -ENOMEM;
233 	}
234 
235 	proc_file = proc_create_data(PROCFS_NAME, 0600, proc_dir,
236 				     &athdiag_fops, (void *)scn);
237 	if (!proc_file) {
238 		remove_proc_entry(PROCFS_NAME, proc_dir);
239 		HIF_ERROR("%s: Could not initialize /proc/%s",
240 			__func__, PROCFS_NAME);
241 		return -ENOMEM;
242 	}
243 
244 	HIF_DBG("/proc/%s/%s created", PROCFS_DIR, PROCFS_NAME);
245 	return 0;               /* everything is ok */
246 }
247 
248 /*
249  * This function is called when the module is unloaded
250  *
251  */
252 void athdiag_procfs_remove(void)
253 {
254 	if (proc_dir) {
255 		remove_proc_entry(PROCFS_NAME, proc_dir);
256 		HIF_DBG("/proc/%s/%s removed", PROCFS_DIR, PROCFS_NAME);
257 		remove_proc_entry(PROCFS_DIR, NULL);
258 		HIF_DBG("/proc/%s removed", PROCFS_DIR);
259 		proc_dir = NULL;
260 	}
261 }
262 #else
263 int athdiag_procfs_init(void *scn)
264 {
265 	return 0;
266 }
267 void athdiag_procfs_remove(void) {}
268 #endif
269