xref: /wlan-dirver/platform/cnss_utils/cnss_utils.c (revision 5b29459b17821e3d9b1e6fcc7bd0a69222e90f53)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2017, 2019, 2021 The Linux Foundation. All rights reserved. */
3 
4 #define pr_fmt(fmt) "cnss_utils: " fmt
5 
6 #include <linux/module.h>
7 #include <linux/kernel.h>
8 #include <linux/slab.h>
9 #include <linux/etherdevice.h>
10 #include <linux/debugfs.h>
11 #include <linux/of.h>
12 #include <net/cnss_utils.h>
13 
14 #define CNSS_MAX_CH_NUM 157
15 struct cnss_unsafe_channel_list {
16 	u16 unsafe_ch_count;
17 	u16 unsafe_ch_list[CNSS_MAX_CH_NUM];
18 };
19 
20 struct cnss_dfs_nol_info {
21 	void *dfs_nol_info;
22 	u16 dfs_nol_info_len;
23 };
24 
25 #define MAX_NO_OF_MAC_ADDR 4
26 #define MAC_PREFIX_LEN 2
27 struct cnss_wlan_mac_addr {
28 	u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
29 	u32 no_of_mac_addr_set;
30 };
31 
32 enum mac_type {
33 	CNSS_MAC_PROVISIONED,
34 	CNSS_MAC_DERIVED,
35 };
36 
37 static struct cnss_utils_priv {
38 	struct cnss_unsafe_channel_list unsafe_channel_list;
39 	struct cnss_dfs_nol_info dfs_nol_info;
40 	/* generic mutex for unsafe channel */
41 	struct mutex unsafe_channel_list_lock;
42 	/* generic spin-lock for dfs_nol info */
43 	spinlock_t dfs_nol_info_lock;
44 	int driver_load_cnt;
45 	struct cnss_wlan_mac_addr wlan_mac_addr;
46 	struct cnss_wlan_mac_addr wlan_der_mac_addr;
47 	enum cnss_utils_cc_src cc_source;
48 	struct dentry *root_dentry;
49 } *cnss_utils_priv;
50 
51 int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
52 				       u16 *unsafe_ch_list, u16 ch_count)
53 {
54 	struct cnss_utils_priv *priv = cnss_utils_priv;
55 
56 	if (!priv)
57 		return -EINVAL;
58 
59 	mutex_lock(&priv->unsafe_channel_list_lock);
60 	if (!unsafe_ch_list || ch_count > CNSS_MAX_CH_NUM) {
61 		mutex_unlock(&priv->unsafe_channel_list_lock);
62 		return -EINVAL;
63 	}
64 
65 	priv->unsafe_channel_list.unsafe_ch_count = ch_count;
66 
67 	if (ch_count == 0)
68 		goto end;
69 
70 	memcpy(priv->unsafe_channel_list.unsafe_ch_list,
71 	       unsafe_ch_list, ch_count * sizeof(u16));
72 
73 end:
74 	mutex_unlock(&priv->unsafe_channel_list_lock);
75 
76 	return 0;
77 }
78 EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
79 
80 int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
81 				       u16 *unsafe_ch_list,
82 				       u16 *ch_count, u16 buf_len)
83 {
84 	struct cnss_utils_priv *priv = cnss_utils_priv;
85 
86 	if (!priv)
87 		return -EINVAL;
88 
89 	mutex_lock(&priv->unsafe_channel_list_lock);
90 	if (!unsafe_ch_list || !ch_count) {
91 		mutex_unlock(&priv->unsafe_channel_list_lock);
92 		return -EINVAL;
93 	}
94 
95 	if (buf_len <
96 	    (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
97 		mutex_unlock(&priv->unsafe_channel_list_lock);
98 		return -ENOMEM;
99 	}
100 
101 	*ch_count = priv->unsafe_channel_list.unsafe_ch_count;
102 	memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
103 	       priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
104 	mutex_unlock(&priv->unsafe_channel_list_lock);
105 
106 	return 0;
107 }
108 EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
109 
110 int cnss_utils_wlan_set_dfs_nol(struct device *dev,
111 				const void *info, u16 info_len)
112 {
113 	void *temp;
114 	void *old_nol_info;
115 	struct cnss_dfs_nol_info *dfs_info;
116 	struct cnss_utils_priv *priv = cnss_utils_priv;
117 
118 	if (!priv)
119 		return -EINVAL;
120 
121 	if (!info || !info_len)
122 		return -EINVAL;
123 
124 	temp = kmemdup(info, info_len, GFP_ATOMIC);
125 	if (!temp)
126 		return -ENOMEM;
127 
128 	spin_lock_bh(&priv->dfs_nol_info_lock);
129 	dfs_info = &priv->dfs_nol_info;
130 	old_nol_info = dfs_info->dfs_nol_info;
131 	dfs_info->dfs_nol_info = temp;
132 	dfs_info->dfs_nol_info_len = info_len;
133 	spin_unlock_bh(&priv->dfs_nol_info_lock);
134 	kfree(old_nol_info);
135 
136 	return 0;
137 }
138 EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
139 
140 int cnss_utils_wlan_get_dfs_nol(struct device *dev,
141 				void *info, u16 info_len)
142 {
143 	int len;
144 	struct cnss_dfs_nol_info *dfs_info;
145 	struct cnss_utils_priv *priv = cnss_utils_priv;
146 
147 	if (!priv)
148 		return -EINVAL;
149 
150 	if (!info || !info_len)
151 		return -EINVAL;
152 
153 	spin_lock_bh(&priv->dfs_nol_info_lock);
154 
155 	dfs_info = &priv->dfs_nol_info;
156 	if (!dfs_info->dfs_nol_info ||
157 	    dfs_info->dfs_nol_info_len == 0) {
158 		spin_unlock_bh(&priv->dfs_nol_info_lock);
159 		return -ENOENT;
160 	}
161 
162 	len = min(info_len, dfs_info->dfs_nol_info_len);
163 	memcpy(info, dfs_info->dfs_nol_info, len);
164 	spin_unlock_bh(&priv->dfs_nol_info_lock);
165 
166 	return len;
167 }
168 EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
169 
170 void cnss_utils_increment_driver_load_cnt(struct device *dev)
171 {
172 	struct cnss_utils_priv *priv = cnss_utils_priv;
173 
174 	if (!priv)
175 		return;
176 
177 	++(priv->driver_load_cnt);
178 }
179 EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
180 
181 int cnss_utils_get_driver_load_cnt(struct device *dev)
182 {
183 	struct cnss_utils_priv *priv = cnss_utils_priv;
184 
185 	if (!priv)
186 		return -EINVAL;
187 
188 	return priv->driver_load_cnt;
189 }
190 EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
191 
192 static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len,
193 				enum mac_type type)
194 {
195 	struct cnss_utils_priv *priv = cnss_utils_priv;
196 	u32 no_of_mac_addr;
197 	struct cnss_wlan_mac_addr *addr = NULL;
198 	int iter;
199 	u8 *temp = NULL;
200 
201 	if (!priv)
202 		return -EINVAL;
203 
204 	if (len == 0 || (len % ETH_ALEN) != 0) {
205 		pr_err("Invalid length %d\n", len);
206 		return -EINVAL;
207 	}
208 
209 	no_of_mac_addr = len / ETH_ALEN;
210 	if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
211 		pr_err("Exceed maximum supported MAC address %u %u\n",
212 		       MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
213 		return -EINVAL;
214 	}
215 
216 	if (type == CNSS_MAC_PROVISIONED)
217 		addr = &priv->wlan_mac_addr;
218 	else
219 		addr = &priv->wlan_der_mac_addr;
220 
221 	if (addr->no_of_mac_addr_set) {
222 		pr_err("WLAN MAC address is already set, num %d type %d\n",
223 		       addr->no_of_mac_addr_set, type);
224 		return 0;
225 	}
226 
227 	addr->no_of_mac_addr_set = no_of_mac_addr;
228 	temp = &addr->mac_addr[0][0];
229 
230 	for (iter = 0; iter < no_of_mac_addr;
231 	     ++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) {
232 		ether_addr_copy(temp, mac_list);
233 		pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
234 			 temp[0], temp[1], temp[2],
235 			 temp[3], temp[4], temp[5]);
236 	}
237 	return 0;
238 }
239 
240 int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len)
241 {
242 	return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED);
243 }
244 EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
245 
246 int cnss_utils_set_wlan_derived_mac_address(const u8 *mac_list,
247 					    const uint32_t len)
248 {
249 	return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED);
250 }
251 EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address);
252 
253 static u8 *get_wlan_mac_address(struct device *dev,
254 				u32 *num, enum mac_type type)
255 {
256 	struct cnss_utils_priv *priv = cnss_utils_priv;
257 	struct cnss_wlan_mac_addr *addr = NULL;
258 
259 	if (!priv)
260 		goto out;
261 
262 	if (type == CNSS_MAC_PROVISIONED)
263 		addr = &priv->wlan_mac_addr;
264 	else
265 		addr = &priv->wlan_der_mac_addr;
266 
267 	if (!addr->no_of_mac_addr_set) {
268 		pr_err("WLAN MAC address is not set, type %d\n", type);
269 		goto out;
270 	}
271 	*num = addr->no_of_mac_addr_set;
272 	return &addr->mac_addr[0][0];
273 
274 out:
275 	*num = 0;
276 	return NULL;
277 }
278 
279 u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
280 {
281 	return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED);
282 }
283 EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
284 
285 u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev,
286 					    uint32_t *num)
287 {
288 	return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED);
289 }
290 EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address);
291 
292 void cnss_utils_set_cc_source(struct device *dev,
293 			      enum cnss_utils_cc_src cc_source)
294 {
295 	struct cnss_utils_priv *priv = cnss_utils_priv;
296 
297 	if (!priv)
298 		return;
299 
300 	priv->cc_source = cc_source;
301 }
302 EXPORT_SYMBOL(cnss_utils_set_cc_source);
303 
304 enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
305 {
306 	struct cnss_utils_priv *priv = cnss_utils_priv;
307 
308 	if (!priv)
309 		return -EINVAL;
310 
311 	return priv->cc_source;
312 }
313 EXPORT_SYMBOL(cnss_utils_get_cc_source);
314 
315 static ssize_t cnss_utils_mac_write(struct file *fp,
316 				    const char __user *user_buf,
317 				    size_t count, loff_t *off)
318 {
319 	struct cnss_utils_priv *priv =
320 		((struct seq_file *)fp->private_data)->private;
321 	char buf[128];
322 	char *input, *mac_type, *mac_address;
323 	u8 *dest_mac;
324 	u8 val;
325 	const char *delim = "\n";
326 	size_t len = 0;
327 	char temp[3] = "";
328 
329 	len = min_t(size_t, count, sizeof(buf) - 1);
330 	if (copy_from_user(buf, user_buf, len))
331 		return -EINVAL;
332 	buf[len] = '\0';
333 
334 	input = buf;
335 
336 	mac_type = strsep(&input, delim);
337 	if (!mac_type)
338 		return -EINVAL;
339 	if (!input)
340 		return -EINVAL;
341 
342 	mac_address = strsep(&input, delim);
343 	if (!mac_address)
344 		return -EINVAL;
345 	if (strcmp("0x", mac_address)) {
346 		pr_err("Invalid MAC prefix\n");
347 		return -EINVAL;
348 	}
349 
350 	len = strlen(mac_address);
351 	mac_address += MAC_PREFIX_LEN;
352 	len -= MAC_PREFIX_LEN;
353 	if (len < ETH_ALEN * 2 || len > ETH_ALEN * 2 * MAX_NO_OF_MAC_ADDR ||
354 	    len % (ETH_ALEN * 2) != 0) {
355 		pr_err("Invalid MAC address length %zu\n", len);
356 		return -EINVAL;
357 	}
358 
359 	if (!strcmp("provisioned", mac_type)) {
360 		dest_mac = &priv->wlan_mac_addr.mac_addr[0][0];
361 		priv->wlan_mac_addr.no_of_mac_addr_set = len / (ETH_ALEN * 2);
362 	} else if (!strcmp("derived", mac_type)) {
363 		dest_mac = &priv->wlan_der_mac_addr.mac_addr[0][0];
364 		priv->wlan_der_mac_addr.no_of_mac_addr_set =
365 			len / (ETH_ALEN * 2);
366 	} else {
367 		pr_err("Invalid MAC address type %s\n", mac_type);
368 		return -EINVAL;
369 	}
370 
371 	while (len--) {
372 		temp[0] = *mac_address++;
373 		temp[1] = *mac_address++;
374 		if (kstrtou8(temp, 16, &val))
375 			return -EINVAL;
376 		*dest_mac++ = val;
377 	}
378 	return count;
379 }
380 
381 static int cnss_utils_mac_show(struct seq_file *s, void *data)
382 {
383 	u8 mac[6];
384 	int i;
385 	struct cnss_utils_priv *priv = s->private;
386 	struct cnss_wlan_mac_addr *addr = NULL;
387 
388 	addr = &priv->wlan_mac_addr;
389 	if (addr->no_of_mac_addr_set) {
390 		seq_puts(s, "\nProvisioned MAC addresseses\n");
391 		for (i = 0; i < addr->no_of_mac_addr_set; i++) {
392 			ether_addr_copy(mac, addr->mac_addr[i]);
393 			seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
394 				   mac[0], mac[1], mac[2],
395 				   mac[3], mac[4], mac[5]);
396 		}
397 	}
398 
399 	addr = &priv->wlan_der_mac_addr;
400 	if (addr->no_of_mac_addr_set) {
401 		seq_puts(s, "\nDerived MAC addresseses\n");
402 		for (i = 0; i < addr->no_of_mac_addr_set; i++) {
403 			ether_addr_copy(mac, addr->mac_addr[i]);
404 			seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
405 				   mac[0], mac[1], mac[2],
406 				   mac[3], mac[4], mac[5]);
407 		}
408 	}
409 
410 	return 0;
411 }
412 
413 static int cnss_utils_mac_open(struct inode *inode, struct file *file)
414 {
415 	return single_open(file, cnss_utils_mac_show, inode->i_private);
416 }
417 
418 static const struct file_operations cnss_utils_mac_fops = {
419 	.read		= seq_read,
420 	.write		= cnss_utils_mac_write,
421 	.release	= single_release,
422 	.open		= cnss_utils_mac_open,
423 	.owner		= THIS_MODULE,
424 	.llseek		= seq_lseek,
425 };
426 
427 static int cnss_utils_debugfs_create(struct cnss_utils_priv *priv)
428 {
429 	int ret = 0;
430 	struct dentry *root_dentry;
431 
432 	root_dentry = debugfs_create_dir("cnss_utils", NULL);
433 
434 	if (IS_ERR(root_dentry)) {
435 		ret = PTR_ERR(root_dentry);
436 		pr_err("Unable to create debugfs %d\n", ret);
437 		goto out;
438 	}
439 	priv->root_dentry = root_dentry;
440 	debugfs_create_file("mac_address", 0600, root_dentry, priv,
441 			    &cnss_utils_mac_fops);
442 out:
443 	return ret;
444 }
445 
446 /**
447  * cnss_utils_is_valid_dt_node_found - Check if valid device tree node present
448  *
449  * Valid device tree node means a node with "qcom,wlan" property present and
450  * "status" property not disabled.
451  *
452  * Return: true if valid device tree node found, false if not found
453  */
454 static bool cnss_utils_is_valid_dt_node_found(void)
455 {
456 	struct device_node *dn = NULL;
457 
458 	for_each_node_with_property(dn, "qcom,wlan") {
459 		if (of_device_is_available(dn))
460 			break;
461 	}
462 
463 	if (dn)
464 		return true;
465 
466 	return false;
467 }
468 
469 static int __init cnss_utils_init(void)
470 {
471 	struct cnss_utils_priv *priv = NULL;
472 
473 	if (!cnss_utils_is_valid_dt_node_found())
474 		return -ENODEV;
475 
476 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
477 	if (!priv)
478 		return -ENOMEM;
479 
480 	priv->cc_source = CNSS_UTILS_SOURCE_CORE;
481 
482 	mutex_init(&priv->unsafe_channel_list_lock);
483 	spin_lock_init(&priv->dfs_nol_info_lock);
484 	cnss_utils_debugfs_create(priv);
485 	cnss_utils_priv = priv;
486 
487 	return 0;
488 }
489 
490 static void __exit cnss_utils_exit(void)
491 {
492 	kfree(cnss_utils_priv);
493 	cnss_utils_priv = NULL;
494 }
495 
496 module_init(cnss_utils_init);
497 module_exit(cnss_utils_exit);
498 
499 MODULE_LICENSE("GPL v2");
500 MODULE_DESCRIPTION("CNSS Utilities Driver");
501