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