1 /*
2  * Copyright (c) 2019-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 /**
20  * DOC: wlan_hdd_debugfs_mibstat.c
21  *
22  * WLAN Host Device Driver implementation to update
23  * debugfs with MIB statistics
24  */
25 
26 #include <cds_sched.h>
27 #include "osif_sync.h"
28 #include <wlan_hdd_debugfs_mibstat.h>
29 #include <wlan_hdd_stats.h>
30 #include <wma_api.h>
31 
32 struct mib_stats_buf {
33 	ssize_t len;
34 	uint8_t *result;
35 };
36 
37 static struct mib_stats_buf mib_stats;
38 
39 qdf_mutex_t mibstats_lock;
40 
hdd_debugfs_process_mib_stats(struct hdd_adapter * adapter,struct stats_event * stats)41 void hdd_debugfs_process_mib_stats(struct hdd_adapter *adapter,
42 				   struct stats_event *stats)
43 {
44 	ssize_t len = 0;
45 	uint8_t *buffer;
46 
47 	hdd_enter();
48 
49 	qdf_mutex_acquire(&mibstats_lock);
50 	if (!mib_stats.result) {
51 		qdf_mutex_release(&mibstats_lock);
52 		hdd_err("MIB statistics buffer is NULL");
53 		return;
54 	}
55 
56 	buffer = mib_stats.result;
57 	buffer += mib_stats.len;
58 
59 	len = scnprintf(buffer, DEBUGFS_MIBSTATS_BUF_SIZE - mib_stats.len,
60 			"dot11RTSSuccessCount %d "
61 			"\ndot11RTSFailureCount %d "
62 			"\ndot11QosFailedCount %d "
63 			"\ndot11QosRetryCount %d "
64 			"\ndot11QosTransmittedFrameCount %d "
65 			"\ndot11QosMPDUsReceivedCount %d "
66 			"\ndot11TransmittedAMPDUCount %d "
67 			"\ndot11QosACKFailureCount %d",
68 			stats->mib_stats->mib_mac_statistics.rts_success_cnt,
69 			stats->mib_stats->mib_mac_statistics.rts_fail_cnt,
70 			stats->mib_stats->mib_qos_counters.qos_failed_cnt,
71 			stats->mib_stats->mib_qos_counters.qos_retry_cnt,
72 			stats->mib_stats->mib_qos_counters.qos_tx_frame_cnt,
73 			stats->mib_stats->mib_qos_counters.qos_mpdu_rx_cnt,
74 			stats->mib_stats->mib_counters_group3.tx_ampdu_cnt,
75 			stats->mib_stats->
76 				mib_qos_counters.tx_qos_ack_fail_cnt_up
77 			);
78 
79 	buffer += len;
80 	mib_stats.len += len;
81 	qdf_mutex_release(&mibstats_lock);
82 
83 	hdd_exit();
84 }
85 
wlan_hdd_mibstats_free_buf(void)86 static inline void wlan_hdd_mibstats_free_buf(void)
87 {
88 	qdf_mutex_acquire(&mibstats_lock);
89 	qdf_mem_free(mib_stats.result);
90 	mib_stats.result = NULL;
91 	mib_stats.len =  0;
92 	qdf_mutex_release(&mibstats_lock);
93 }
94 
wlan_hdd_mibstats_alloc_buf(void)95 static int wlan_hdd_mibstats_alloc_buf(void)
96 {
97 	qdf_mutex_acquire(&mibstats_lock);
98 	if (mib_stats.result) {
99 		qdf_mutex_release(&mibstats_lock);
100 		hdd_err("Buffer is already allocated");
101 		return 0;
102 	}
103 	mib_stats.len = 0;
104 	mib_stats.result = qdf_mem_malloc(DEBUGFS_MIBSTATS_BUF_SIZE);
105 	if (!mib_stats.result) {
106 		qdf_mutex_release(&mibstats_lock);
107 		return -EINVAL;
108 	}
109 	qdf_mutex_release(&mibstats_lock);
110 	return 0;
111 }
112 
113 /**
114  * hdd_debugfs_mib_stats_update() - Update userspace with local stats buffer
115  * @buf: userspace buffer (to which data is being copied into)
116  * @count: max data that can be copied into buf in bytes
117  * @pos: offset (where data should be copied into)
118  *
119  * This function copies mib statistics buffer into debugfs
120  * entry.
121  *
122  * Return: number of characters copied; 0 on no-copy
123  */
hdd_debugfs_mib_stats_update(char __user * buf,size_t count,loff_t * pos)124 static ssize_t hdd_debugfs_mib_stats_update(char __user *buf,
125 					    size_t count, loff_t *pos)
126 {
127 	ssize_t ret_cnt;
128 
129 	hdd_enter();
130 	qdf_mutex_acquire(&mibstats_lock);
131 	if (!mib_stats.result) {
132 		qdf_mutex_release(&mibstats_lock);
133 		hdd_err("Trying to read from NULL buffer");
134 		return 0;
135 	}
136 
137 	ret_cnt = simple_read_from_buffer(buf, count, pos,
138 					  mib_stats.result,
139 					  mib_stats.len);
140 	qdf_mutex_release(&mibstats_lock);
141 	hdd_debug("mib stats read req: count: %zu, pos: %lld", count, *pos);
142 
143 	hdd_exit();
144 	return ret_cnt;
145 }
146 
147 /**
148  * __wlan_hdd_release_mib_stats_debugfs() - Function to free private
149  *                                          memory on release
150  * @net_dev: net_device context used to register the debugfs file
151  *
152  * Return: Errno
153  */
__wlan_hdd_release_mib_stats_debugfs(struct net_device * net_dev)154 static int __wlan_hdd_release_mib_stats_debugfs(struct net_device *net_dev)
155 
156 {
157 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
158 	struct hdd_context *hdd_ctx;
159 	int errno;
160 
161 	hdd_enter_dev(net_dev);
162 
163 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
164 	errno = wlan_hdd_validate_context(hdd_ctx);
165 	if (errno)
166 		return errno;
167 
168 	wlan_hdd_mibstats_free_buf();
169 
170 	hdd_exit();
171 
172 	return 0;
173 }
174 
175 /**
176  * wlan_hdd_release_mib_stats_debugfs() - SSR wrapper function to free
177  *                                        private memory on release
178  * @inode: Pointer to inode structure
179  * @file: file pointer
180  *
181  * Return: Errno
182  */
wlan_hdd_release_mib_stats_debugfs(struct inode * inode,struct file * file)183 static int wlan_hdd_release_mib_stats_debugfs(struct inode *inode,
184 					      struct file *file)
185 {
186 	struct net_device *net_dev = file_inode(file)->i_private;
187 	struct osif_vdev_sync *vdev_sync;
188 	int errno;
189 
190 	errno = osif_vdev_sync_op_start(net_dev, &vdev_sync);
191 	if (errno)
192 		return errno;
193 
194 	errno = __wlan_hdd_release_mib_stats_debugfs(net_dev);
195 
196 	osif_vdev_sync_op_stop(vdev_sync);
197 
198 	return errno;
199 }
200 
201 /**
202  * __wlan_hdd_read_mib_stats_debugfs() - mib_stats debugfs handler
203  * @net_dev: net_device context used to register the debugfs file
204  * @buf: text being written to the debugfs
205  * @count: size of @buf
206  * @pos: (unused) offset into the virtual file system
207  *
208  * Return: Number of bytes read on success, error number otherwise
209  */
__wlan_hdd_read_mib_stats_debugfs(struct net_device * net_dev,char __user * buf,size_t count,loff_t * pos)210 static ssize_t __wlan_hdd_read_mib_stats_debugfs(struct net_device *net_dev,
211 						 char __user *buf,
212 						 size_t count, loff_t *pos)
213 
214 {
215 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
216 	struct hdd_context *hdd_ctx;
217 	ssize_t ret;
218 
219 	hdd_enter_dev(net_dev);
220 
221 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
222 	ret = wlan_hdd_validate_context(hdd_ctx);
223 	if (ret)
224 		goto free_buf;
225 
226 	if (*pos == 0) {
227 		ret = wlan_hdd_get_mib_stats(adapter);
228 		if (ret)
229 			goto free_buf;
230 	}
231 
232 	/* All the events are received and buffer is populated */
233 	ret = hdd_debugfs_mib_stats_update(buf, count, pos);
234 	hdd_debug("%zu characters written into debugfs", ret);
235 
236 	hdd_exit();
237 
238 	return ret;
239 
240 free_buf:
241 	wlan_hdd_mibstats_free_buf();
242 
243 	hdd_exit();
244 
245 	return ret;
246 }
247 
248 /**
249  * wlan_hdd_read_mib_stats_debugfs() - SSR wrapper function to read
250  *                                     mib stats
251  * @file: file pointer
252  * @buf: buffer
253  * @count: count
254  * @pos: position pointer
255  *
256  * Return: Number of bytes read on success, error number otherwise
257  */
wlan_hdd_read_mib_stats_debugfs(struct file * file,char __user * buf,size_t count,loff_t * pos)258 static ssize_t wlan_hdd_read_mib_stats_debugfs(struct file *file,
259 				   char __user *buf, size_t count,
260 				   loff_t *pos)
261 {
262 	struct net_device *net_dev = file_inode(file)->i_private;
263 	struct osif_vdev_sync *vdev_sync;
264 	ssize_t err_size;
265 
266 	err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync);
267 	if (err_size)
268 		return err_size;
269 
270 	err_size = __wlan_hdd_read_mib_stats_debugfs(net_dev, buf,
271 						     count, pos);
272 
273 	osif_vdev_sync_op_stop(vdev_sync);
274 
275 	return err_size;
276 }
277 
278 /**
279  * __wlan_hdd_open_mib_stats_debugfs() - Function to save private on open
280  * @net_dev: net_device context used to register the debugfs file
281  *
282  * Return: Errno
283  */
__wlan_hdd_open_mib_stats_debugfs(struct net_device * net_dev)284 static int __wlan_hdd_open_mib_stats_debugfs(struct net_device *net_dev)
285 {
286 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
287 	struct hdd_context *hdd_ctx;
288 	int errno;
289 
290 	hdd_enter_dev(net_dev);
291 
292 	errno = hdd_validate_adapter(adapter);
293 	if (errno)
294 		return errno;
295 
296 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
297 	errno = wlan_hdd_validate_context(hdd_ctx);
298 	if (errno)
299 		return errno;
300 
301 	errno = wlan_hdd_mibstats_alloc_buf();
302 	if (errno)
303 		return errno;
304 
305 	hdd_exit();
306 
307 	return 0;
308 }
309 
310 /**
311  * wlan_hdd_open_mib_stats_debugfs() - SSR wrapper to save private
312  *                                     on open
313  * @inode: Pointer to inode structure
314  * @file: file pointer
315  *
316  * Return: Errno
317  */
wlan_hdd_open_mib_stats_debugfs(struct inode * inode,struct file * file)318 static int wlan_hdd_open_mib_stats_debugfs(struct inode *inode,
319 			       struct file *file)
320 {
321 	struct net_device *net_dev = inode->i_private;
322 	struct osif_vdev_sync *vdev_sync;
323 	int errno;
324 
325 	errno = osif_vdev_sync_op_start(net_dev, &vdev_sync);
326 	if (errno)
327 		return errno;
328 
329 	errno = __wlan_hdd_open_mib_stats_debugfs(net_dev);
330 
331 	osif_vdev_sync_op_stop(vdev_sync);
332 
333 	return errno;
334 }
335 
336 static const struct file_operations fops_mib_stats = {
337 	.read = wlan_hdd_read_mib_stats_debugfs,
338 	.open = wlan_hdd_open_mib_stats_debugfs,
339 	.release = wlan_hdd_release_mib_stats_debugfs,
340 	.owner = THIS_MODULE,
341 	.llseek = default_llseek,
342 };
343 
wlan_hdd_create_mib_stats_file(struct hdd_adapter * adapter)344 int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter)
345 {
346 	if (!debugfs_create_file("mib_stats", 0444, adapter->debugfs_phy,
347 				 adapter->dev, &fops_mib_stats))
348 		return -EINVAL;
349 
350 	return 0;
351 }
352 
wlan_hdd_create_mib_stats_lock(void)353 void wlan_hdd_create_mib_stats_lock(void)
354 {
355 	if (QDF_IS_STATUS_ERROR(qdf_mutex_create(
356 				&mibstats_lock)))
357 		hdd_err("mibstats lock init failed!");
358 }
359 
wlan_hdd_destroy_mib_stats_lock(void)360 void wlan_hdd_destroy_mib_stats_lock(void)
361 {
362 	qdf_mutex_destroy(&mibstats_lock);
363 }
364