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