1 /*
2  * Copyright (c) 2018-2020 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 /**
21  * DOC: wlan_hdd_debugfs_csr.c
22  *
23  * WLAN Host Device Driver implementation to update
24  * debugfs with roaming related information
25  */
26 
27 #include "osif_sync.h"
28 #include <wlan_hdd_debugfs_csr.h>
29 #include <wlan_hdd_main.h>
30 #include <cds_sched.h>
31 #include <wma_api.h>
32 #include "qwlan_version.h"
33 #include "wmi_unified_param.h"
34 #include "wlan_hdd_debugfs.h"
35 
36 ssize_t
wlan_hdd_current_time_info_debugfs(uint8_t * buf,ssize_t buf_avail_len)37 wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len)
38 {
39 	ssize_t length;
40 	char time_buffer[HDD_TIME_STRING_LEN];
41 	int ret_val;
42 
43 	qdf_get_time_of_the_day_in_hr_min_sec_usec(time_buffer,
44 						   sizeof(time_buffer));
45 	ret_val = scnprintf(buf, buf_avail_len,
46 			    "\nTime at which this file generated = %s\n",
47 			    time_buffer);
48 	if (ret_val < 0)
49 		return 0;
50 	length = ret_val;
51 
52 	return length;
53 }
54 
55 /**
56  * wlan_hdd_debugfs_update_csr() - Function to update internal debugfs buffer
57  * and write into user-space buffer
58  * @hdd_ctx: hdd context
59  * @adapter: adapter
60  * @id: used to identify file for which this info has to be read
61  * @buf: output buffer to write
62  * @buf_avail_len: length of the available buffer
63  *
64  * Return: Number of bytes read on success, zero otherwise
65  */
66 static ssize_t
wlan_hdd_debugfs_update_csr(struct hdd_context * hdd_ctx,struct hdd_adapter * adapter,enum hdd_debugfs_file_id id,uint8_t * buf,ssize_t buf_avail_len)67 wlan_hdd_debugfs_update_csr(struct hdd_context *hdd_ctx,
68 			    struct hdd_adapter *adapter,
69 			    enum hdd_debugfs_file_id id,
70 			    uint8_t *buf,
71 			    ssize_t buf_avail_len)
72 {
73 	ssize_t len = 0;
74 
75 	switch (id) {
76 	case HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO:
77 		/* populate roam scan stats info */
78 		len = wlan_hdd_debugfs_update_roam_stats(hdd_ctx, adapter,
79 							 buf, buf_avail_len);
80 		break;
81 	case HDD_DEBUFS_FILE_ID_OFFLOAD_INFO:
82 		/* populate offload info */
83 		len = wlan_hdd_debugfs_update_filters_info(hdd_ctx, adapter,
84 							   buf, buf_avail_len);
85 		break;
86 	default:
87 		hdd_err("Failed to fetch stats, unknown stats type");
88 	}
89 
90 	return len;
91 }
92 
93 /**
94  * __wlan_hdd_read_debugfs_csr() - Function to read debug stats
95  * @info: buffer info allocated when the debugfs file was opened
96  * @buf: buffer
97  * @count: count
98  * @pos: position pointer
99  *
100  * Return: Number of bytes read on success, zero otherwise
101  */
102 static ssize_t
__wlan_hdd_read_debugfs_csr(struct wlan_hdd_debugfs_buffer_info * info,char __user * buf,size_t count,loff_t * pos)103 __wlan_hdd_read_debugfs_csr(struct wlan_hdd_debugfs_buffer_info *info,
104 			    char __user *buf, size_t count, loff_t *pos)
105 {
106 	struct hdd_adapter *adapter = info->adapter;
107 	struct hdd_context *hdd_ctx;
108 	int ret;
109 	ssize_t length;
110 
111 	hdd_enter();
112 
113 	if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
114 		hdd_err("Invalid adapter or adapter has invalid magic");
115 		return 0;
116 	}
117 
118 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
119 	ret = wlan_hdd_validate_context(hdd_ctx);
120 	if (ret)
121 		return 0;
122 
123 	if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) {
124 		hdd_err("Interface is not enabled");
125 		return 0;
126 	}
127 
128 	if (*pos == 0) {
129 		info->length = wlan_hdd_debugfs_update_csr(hdd_ctx, adapter,
130 							   info->id,
131 							   info->data,
132 							   info->max_buf_len);
133 	}
134 
135 	length = simple_read_from_buffer(buf, count, pos,
136 					 info->data, info->length);
137 	hdd_debug("length written = %zu, count: %zu, pos: %lld",
138 		  length, count, *pos);
139 
140 	hdd_exit();
141 	return length;
142 }
143 
144 /**
145  * wlan_hdd_read_debugfs_csr() - SSR wrapper function to read stats
146  * @file: file pointer
147  * @buf: buffer
148  * @count: count
149  * @pos: position pointer
150  *
151  * Return: Number of bytes read on success, zero otherwise
152  */
153 static ssize_t
wlan_hdd_read_debugfs_csr(struct file * file,char __user * buf,size_t count,loff_t * pos)154 wlan_hdd_read_debugfs_csr(struct file *file, char __user *buf,
155 			  size_t count, loff_t *pos)
156 {
157 	struct wlan_hdd_debugfs_buffer_info *info = file->private_data;
158 	struct osif_vdev_sync *vdev_sync;
159 	ssize_t err_size;
160 
161 	err_size = osif_vdev_sync_op_start(info->adapter->dev, &vdev_sync);
162 	if (err_size)
163 		return err_size;
164 
165 	err_size = __wlan_hdd_read_debugfs_csr(info, buf, count, pos);
166 
167 	osif_vdev_sync_op_stop(vdev_sync);
168 
169 	return err_size;
170 }
171 
172 /**
173  * __wlan_hdd_open_debugfs_csr() - Allocates memory for private data
174  * @adapter: the HDD adapter to operate against
175  * @csr: file info used to register the debugfs file
176  * @file: file pointer
177  *
178  * Return: Errno
179  */
__wlan_hdd_open_debugfs_csr(struct hdd_adapter * adapter,struct hdd_debugfs_file_info * csr,struct file * file)180 static int __wlan_hdd_open_debugfs_csr(struct hdd_adapter *adapter,
181 				       struct hdd_debugfs_file_info *csr,
182 				       struct file *file)
183 {
184 	struct wlan_hdd_debugfs_buffer_info *info;
185 	struct hdd_context *hdd_ctx;
186 	int ret;
187 
188 	hdd_enter();
189 
190 	if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) {
191 		hdd_err("Invalid adapter or adapter has invalid magic");
192 		return -EINVAL;
193 	}
194 
195 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
196 	ret = wlan_hdd_validate_context(hdd_ctx);
197 	if (ret)
198 		return -EINVAL;
199 
200 	if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) {
201 		hdd_err("Interface is not enabled");
202 		return -EINVAL;
203 	}
204 
205 	info = qdf_mem_malloc(sizeof(*info));
206 	if (!info)
207 		return -ENOMEM;
208 
209 	info->data = qdf_mem_malloc(csr->buf_max_size);
210 	if (!info->data) {
211 		qdf_mem_free(info);
212 		return -ENOMEM;
213 	}
214 	info->length = 0;
215 	info->max_buf_len = csr->buf_max_size;
216 	info->id = csr->id;
217 	info->adapter = adapter;
218 
219 	file->private_data = info;
220 	hdd_exit();
221 
222 	return 0;
223 }
224 
225 /**
226  * wlan_hdd_open_debugfs_csr() - SSR wrapper function to allocate memory for
227  *	private data on file open
228  * @inode: Pointer to inode structure
229  * @file: file pointer
230  *
231  * Return: Errno
232  */
wlan_hdd_open_debugfs_csr(struct inode * inode,struct file * file)233 static int wlan_hdd_open_debugfs_csr(struct inode *inode, struct file *file)
234 {
235 	struct hdd_debugfs_file_info *csr = inode->i_private;
236 	struct hdd_adapter *adapter = qdf_container_of(csr, struct hdd_adapter,
237 						       csr_file[csr->id]);
238 	struct osif_vdev_sync *vdev_sync;
239 	int errno;
240 
241 	errno = osif_vdev_sync_op_start(adapter->dev, &vdev_sync);
242 	if (errno)
243 		return errno;
244 
245 	hdd_debugfs_thread_increment();
246 	errno = __wlan_hdd_open_debugfs_csr(adapter, csr, file);
247 	if (errno)
248 		hdd_debugfs_thread_decrement();
249 
250 	osif_vdev_sync_op_stop(vdev_sync);
251 
252 	return errno;
253 }
254 
255 /**
256  * __wlan_hdd_release_debugfs_csr() - Function to free private memory on
257  *	release
258  * @file: file pointer
259  *
260  * Return: Errno
261  */
262 static int
__wlan_hdd_release_debugfs_csr(struct file * file)263 __wlan_hdd_release_debugfs_csr(struct file *file)
264 {
265 	struct wlan_hdd_debugfs_buffer_info *info = file->private_data;
266 
267 	hdd_enter();
268 
269 	file->private_data = NULL;
270 	qdf_mem_free(info->data);
271 	qdf_mem_free(info);
272 
273 	hdd_exit();
274 
275 	return 0;
276 }
277 
278 /**
279  * wlan_hdd_release_debugfs_csr() - SSR wrapper function to free
280  * private data on release
281  * @inode: Pointer to inode structure
282  * @file: file pointer
283  *
284  * Return: Errno
285  */
wlan_hdd_release_debugfs_csr(struct inode * inode,struct file * file)286 static int wlan_hdd_release_debugfs_csr(struct inode *inode, struct file *file)
287 {
288 	struct wlan_hdd_debugfs_buffer_info *info = file->private_data;
289 	struct osif_vdev_sync *vdev_sync;
290 	int errno;
291 
292 	errno = osif_vdev_sync_op_start(info->adapter->dev, &vdev_sync);
293 	if (errno)
294 		return errno;
295 
296 	errno = __wlan_hdd_release_debugfs_csr(file);
297 	hdd_debugfs_thread_decrement();
298 
299 	osif_vdev_sync_op_stop(vdev_sync);
300 
301 	return errno;
302 }
303 
304 static const struct file_operations fops_csr_debugfs = {
305 	.read = wlan_hdd_read_debugfs_csr,
306 	.open = wlan_hdd_open_debugfs_csr,
307 	.release = wlan_hdd_release_debugfs_csr,
308 	.owner = THIS_MODULE,
309 	.llseek = default_llseek,
310 };
311 
wlan_hdd_debugfs_csr_init(struct hdd_adapter * adapter)312 void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter)
313 {
314 	struct hdd_debugfs_file_info *csr;
315 	const uint32_t max_len = HDD_DEBUGFS_FILE_NAME_MAX;
316 
317 	/*
318 	 * Create debugfs diagnostic files for connect, offload info
319 	 * and roam info and store in csr_file member of adapter
320 	 */
321 
322 	csr = &adapter->csr_file[HDD_DEBUFS_FILE_ID_OFFLOAD_INFO];
323 	if (!csr->entry) {
324 		strlcpy(csr->name, "offload_info", max_len);
325 		csr->id = HDD_DEBUFS_FILE_ID_OFFLOAD_INFO;
326 		csr->buf_max_size = DEBUGFS_OFFLOAD_INFO_BUF_SIZE;
327 		csr->entry = debugfs_create_file(csr->name, 0444,
328 						 adapter->debugfs_phy,
329 						 csr, &fops_csr_debugfs);
330 		if (!csr->entry)
331 			hdd_err("Failed to create generic_info debugfs file");
332 	}
333 
334 	csr = &adapter->csr_file[HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO];
335 	if (!csr->entry) {
336 		strlcpy(csr->name, "roam_stats", max_len);
337 		csr->id = HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO;
338 		csr->buf_max_size = DEBUGFS_ROAM_SCAN_STATS_INFO_BUF_SIZE;
339 		csr->entry = debugfs_create_file(csr->name, 0444,
340 						 adapter->debugfs_phy,
341 						 csr, &fops_csr_debugfs);
342 		if (!csr->entry)
343 			hdd_err("Failed to create generic_info debugfs file");
344 	}
345 }
346 
wlan_hdd_debugfs_csr_deinit(struct hdd_adapter * adapter)347 void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter)
348 {
349 	uint32_t i;
350 	struct dentry *entry;
351 
352 	for (i = 0; i < HDD_DEBUGFS_FILE_ID_MAX; i++) {
353 		entry = adapter->csr_file[i].entry;
354 		if (!entry)
355 			continue;
356 
357 		adapter->csr_file[i].entry = NULL;
358 		debugfs_remove(entry);
359 		entry = NULL;
360 	}
361 }
362