blob: 77a58c8cb6a40fb3ba23ea0af76d4a69aaf9c3df [file] [log] [blame]
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -07001/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "cnss_utils: " fmt
14
15#include <linux/module.h>
Sameer Thalappild7ab5882017-11-30 17:31:16 -080016#include <linux/kernel.h>
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070017#include <linux/slab.h>
18#include <linux/etherdevice.h>
Sameer Thalappild7ab5882017-11-30 17:31:16 -080019#include <linux/debugfs.h>
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070020#include <net/cnss_utils.h>
21
22#define CNSS_MAX_CH_NUM 45
23struct cnss_unsafe_channel_list {
24 u16 unsafe_ch_count;
25 u16 unsafe_ch_list[CNSS_MAX_CH_NUM];
26};
27
28struct cnss_dfs_nol_info {
29 void *dfs_nol_info;
30 u16 dfs_nol_info_len;
31};
32
33#define MAX_NO_OF_MAC_ADDR 4
Sameer Thalappild7ab5882017-11-30 17:31:16 -080034#define MAC_PREFIX_LEN 2
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070035struct cnss_wlan_mac_addr {
36 u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
37 u32 no_of_mac_addr_set;
38};
39
Sameer Thalappil1a15d712017-11-08 17:08:48 -080040enum mac_type {
41 CNSS_MAC_PROVISIONED,
42 CNSS_MAC_DERIVED,
43};
44
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070045static struct cnss_utils_priv {
46 struct cnss_unsafe_channel_list unsafe_channel_list;
47 struct cnss_dfs_nol_info dfs_nol_info;
48 /* generic mutex for unsafe channel */
49 struct mutex unsafe_channel_list_lock;
50 /* generic spin-lock for dfs_nol info */
51 spinlock_t dfs_nol_info_lock;
52 int driver_load_cnt;
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070053 struct cnss_wlan_mac_addr wlan_mac_addr;
Sameer Thalappil1a15d712017-11-08 17:08:48 -080054 struct cnss_wlan_mac_addr wlan_der_mac_addr;
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070055 enum cnss_utils_cc_src cc_source;
Sameer Thalappild7ab5882017-11-30 17:31:16 -080056 struct dentry *root_dentry;
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070057} *cnss_utils_priv;
58
59int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
60 u16 *unsafe_ch_list, u16 ch_count)
61{
62 struct cnss_utils_priv *priv = cnss_utils_priv;
63
64 if (!priv)
65 return -EINVAL;
66
67 mutex_lock(&priv->unsafe_channel_list_lock);
68 if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) {
69 mutex_unlock(&priv->unsafe_channel_list_lock);
70 return -EINVAL;
71 }
72
73 priv->unsafe_channel_list.unsafe_ch_count = ch_count;
74
75 if (ch_count == 0)
76 goto end;
77
78 memcpy(priv->unsafe_channel_list.unsafe_ch_list,
79 unsafe_ch_list, ch_count * sizeof(u16));
80
81end:
82 mutex_unlock(&priv->unsafe_channel_list_lock);
83
84 return 0;
85}
86EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
87
88int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
89 u16 *unsafe_ch_list,
90 u16 *ch_count, u16 buf_len)
91{
92 struct cnss_utils_priv *priv = cnss_utils_priv;
93
94 if (!priv)
95 return -EINVAL;
96
97 mutex_lock(&priv->unsafe_channel_list_lock);
98 if (!unsafe_ch_list || !ch_count) {
99 mutex_unlock(&priv->unsafe_channel_list_lock);
100 return -EINVAL;
101 }
102
103 if (buf_len <
104 (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
105 mutex_unlock(&priv->unsafe_channel_list_lock);
106 return -ENOMEM;
107 }
108
109 *ch_count = priv->unsafe_channel_list.unsafe_ch_count;
110 memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
111 priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
112 mutex_unlock(&priv->unsafe_channel_list_lock);
113
114 return 0;
115}
116EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
117
118int cnss_utils_wlan_set_dfs_nol(struct device *dev,
119 const void *info, u16 info_len)
120{
121 void *temp;
122 void *old_nol_info;
123 struct cnss_dfs_nol_info *dfs_info;
124 struct cnss_utils_priv *priv = cnss_utils_priv;
125
126 if (!priv)
127 return -EINVAL;
128
129 if (!info || !info_len)
130 return -EINVAL;
131
132 temp = kmalloc(info_len, GFP_ATOMIC);
133 if (!temp)
134 return -ENOMEM;
135
136 memcpy(temp, info, info_len);
137 spin_lock_bh(&priv->dfs_nol_info_lock);
138 dfs_info = &priv->dfs_nol_info;
139 old_nol_info = dfs_info->dfs_nol_info;
140 dfs_info->dfs_nol_info = temp;
141 dfs_info->dfs_nol_info_len = info_len;
142 spin_unlock_bh(&priv->dfs_nol_info_lock);
143 kfree(old_nol_info);
144
145 return 0;
146}
147EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
148
149int cnss_utils_wlan_get_dfs_nol(struct device *dev,
150 void *info, u16 info_len)
151{
152 int len;
153 struct cnss_dfs_nol_info *dfs_info;
154 struct cnss_utils_priv *priv = cnss_utils_priv;
155
156 if (!priv)
157 return -EINVAL;
158
159 if (!info || !info_len)
160 return -EINVAL;
161
162 spin_lock_bh(&priv->dfs_nol_info_lock);
163
164 dfs_info = &priv->dfs_nol_info;
165 if (!dfs_info->dfs_nol_info ||
166 dfs_info->dfs_nol_info_len == 0) {
167 spin_unlock_bh(&priv->dfs_nol_info_lock);
168 return -ENOENT;
169 }
170
171 len = min(info_len, dfs_info->dfs_nol_info_len);
172 memcpy(info, dfs_info->dfs_nol_info, len);
173 spin_unlock_bh(&priv->dfs_nol_info_lock);
174
175 return len;
176}
177EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
178
179void cnss_utils_increment_driver_load_cnt(struct device *dev)
180{
181 struct cnss_utils_priv *priv = cnss_utils_priv;
182
183 if (!priv)
184 return;
185
186 ++(priv->driver_load_cnt);
187}
188EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
189
190int cnss_utils_get_driver_load_cnt(struct device *dev)
191{
192 struct cnss_utils_priv *priv = cnss_utils_priv;
193
194 if (!priv)
195 return -EINVAL;
196
197 return priv->driver_load_cnt;
198}
199EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
200
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800201static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len,
202 enum mac_type type)
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700203{
204 struct cnss_utils_priv *priv = cnss_utils_priv;
205 u32 no_of_mac_addr;
206 struct cnss_wlan_mac_addr *addr = NULL;
207 int iter;
208 u8 *temp = NULL;
209
210 if (!priv)
211 return -EINVAL;
212
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700213 if (len == 0 || (len % ETH_ALEN) != 0) {
214 pr_err("Invalid length %d\n", len);
215 return -EINVAL;
216 }
217
218 no_of_mac_addr = len / ETH_ALEN;
219 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
220 pr_err("Exceed maximum supported MAC address %u %u\n",
221 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
222 return -EINVAL;
223 }
224
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800225 if (type == CNSS_MAC_PROVISIONED)
226 addr = &priv->wlan_mac_addr;
227 else
228 addr = &priv->wlan_der_mac_addr;
229
230 if (addr->no_of_mac_addr_set) {
231 pr_err("WLAN MAC address is already set, num %d type %d\n",
232 addr->no_of_mac_addr_set, type);
233 return 0;
234 }
235
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700236 addr->no_of_mac_addr_set = no_of_mac_addr;
237 temp = &addr->mac_addr[0][0];
238
239 for (iter = 0; iter < no_of_mac_addr;
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800240 ++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) {
241 ether_addr_copy(temp, mac_list);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700242 pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
243 temp[0], temp[1], temp[2],
244 temp[3], temp[4], temp[5]);
245 }
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700246 return 0;
247}
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800248
249int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len)
250{
251 return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED);
252}
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700253EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
254
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800255int cnss_utils_set_wlan_derived_mac_address(
256 const u8 *mac_list, const uint32_t len)
257{
258 return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED);
259}
260EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address);
261
262static u8 *get_wlan_mac_address(struct device *dev,
263 u32 *num, enum mac_type type)
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700264{
265 struct cnss_utils_priv *priv = cnss_utils_priv;
266 struct cnss_wlan_mac_addr *addr = NULL;
267
268 if (!priv)
269 goto out;
270
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800271 if (type == CNSS_MAC_PROVISIONED)
272 addr = &priv->wlan_mac_addr;
273 else
274 addr = &priv->wlan_der_mac_addr;
275
276 if (!addr->no_of_mac_addr_set) {
277 pr_err("WLAN MAC address is not set, type %d\n", type);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700278 goto out;
279 }
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700280 *num = addr->no_of_mac_addr_set;
281 return &addr->mac_addr[0][0];
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800282
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700283out:
284 *num = 0;
285 return NULL;
286}
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800287
288u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
289{
290 return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED);
291}
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700292EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
293
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800294u8 *cnss_utils_get_wlan_derived_mac_address(
295 struct device *dev, uint32_t *num)
296{
297 return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED);
298}
299EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address);
300
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700301void cnss_utils_set_cc_source(struct device *dev,
302 enum cnss_utils_cc_src cc_source)
303{
304 struct cnss_utils_priv *priv = cnss_utils_priv;
305
306 if (!priv)
307 return;
308
309 priv->cc_source = cc_source;
310}
311EXPORT_SYMBOL(cnss_utils_set_cc_source);
312
313enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
314{
315 struct cnss_utils_priv *priv = cnss_utils_priv;
316
317 if (!priv)
318 return -EINVAL;
319
320 return priv->cc_source;
321}
322EXPORT_SYMBOL(cnss_utils_get_cc_source);
323
Sameer Thalappild7ab5882017-11-30 17:31:16 -0800324static ssize_t cnss_utils_mac_write(struct file *fp,
325 const char __user *user_buf,
326 size_t count, loff_t *off)
327{
328 struct cnss_utils_priv *priv =
329 ((struct seq_file *)fp->private_data)->private;
330 char buf[128];
331 char *input, *mac_type, *mac_address;
332 u8 *dest_mac;
333 u8 val;
334 const char *delim = " \n";
335 size_t len = 0;
336 char temp[3] = "";
337
338 len = min_t(size_t, count, sizeof(buf) - 1);
339 if (copy_from_user(buf, user_buf, len))
340 return -EINVAL;
341 buf[len] = '\0';
342
343 input = buf;
344
345 mac_type = strsep(&input, delim);
346 if (!mac_type)
347 return -EINVAL;
348 if (!input)
349 return -EINVAL;
350
351 mac_address = strsep(&input, delim);
352 if (!mac_address)
353 return -EINVAL;
354 if (strncmp("0x", mac_address, MAC_PREFIX_LEN)) {
355 pr_err("Invalid MAC prefix\n");
356 return -EINVAL;
357 }
358
359 len = strlen(mac_address);
360 mac_address += MAC_PREFIX_LEN;
361 len -= MAC_PREFIX_LEN;
362 if (len < ETH_ALEN * 2 || len > ETH_ALEN * 2 * MAX_NO_OF_MAC_ADDR ||
363 len % (ETH_ALEN * 2) != 0) {
364 pr_err("Invalid MAC address length %zu\n", len);
365 return -EINVAL;
366 }
367
368 if (!strcmp("provisioned", mac_type)) {
369 dest_mac = &priv->wlan_mac_addr.mac_addr[0][0];
370 priv->wlan_mac_addr.no_of_mac_addr_set = len / (ETH_ALEN * 2);
371 } else if (!strcmp("derived", mac_type)) {
372 dest_mac = &priv->wlan_der_mac_addr.mac_addr[0][0];
373 priv->wlan_der_mac_addr.no_of_mac_addr_set =
374 len / (ETH_ALEN * 2);
375 } else {
376 pr_err("Invalid MAC address type %s\n", mac_type);
377 return -EINVAL;
378 }
379
380 while (len--) {
381 temp[0] = *mac_address++;
382 temp[1] = *mac_address++;
383 if (kstrtou8(temp, 16, &val))
384 return -EINVAL;
385 *dest_mac++ = val;
386 }
387 return count;
388}
389
390static int cnss_utils_mac_show(struct seq_file *s, void *data)
391{
392 u8 mac[6];
393 int i;
394 struct cnss_utils_priv *priv = s->private;
395 struct cnss_wlan_mac_addr *addr = NULL;
396
397 addr = &priv->wlan_mac_addr;
398 if (addr->no_of_mac_addr_set) {
399 seq_puts(s, "\nProvisioned MAC addresseses\n");
400 for (i = 0; i < addr->no_of_mac_addr_set; i++) {
401 ether_addr_copy(mac, addr->mac_addr[i]);
402 seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
403 mac[0], mac[1], mac[2],
404 mac[3], mac[4], mac[5]);
405 }
406 }
407
408 addr = &priv->wlan_der_mac_addr;
409 if (addr->no_of_mac_addr_set) {
410 seq_puts(s, "\nDerived MAC addresseses\n");
411 for (i = 0; i < addr->no_of_mac_addr_set; i++) {
412 ether_addr_copy(mac, addr->mac_addr[i]);
413 seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
414 mac[0], mac[1], mac[2],
415 mac[3], mac[4], mac[5]);
416 }
417 }
418
419 return 0;
420}
421
422static int cnss_utils_mac_open(struct inode *inode, struct file *file)
423{
424 return single_open(file, cnss_utils_mac_show, inode->i_private);
425}
426
427static const struct file_operations cnss_utils_mac_fops = {
428 .read = seq_read,
429 .write = cnss_utils_mac_write,
430 .release = single_release,
431 .open = cnss_utils_mac_open,
432 .owner = THIS_MODULE,
433 .llseek = seq_lseek,
434};
435
436static int cnss_utils_debugfs_create(struct cnss_utils_priv *priv)
437{
438 int ret = 0;
439 struct dentry *root_dentry;
440
441 root_dentry = debugfs_create_dir("cnss_utils", NULL);
442
443 if (IS_ERR(root_dentry)) {
444 ret = PTR_ERR(root_dentry);
445 pr_err("Unable to create debugfs %d\n", ret);
446 goto out;
447 }
448 priv->root_dentry = root_dentry;
449 debugfs_create_file("mac_address", 0600, root_dentry, priv,
450 &cnss_utils_mac_fops);
451out:
452 return ret;
453}
454
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700455static int __init cnss_utils_init(void)
456{
457 struct cnss_utils_priv *priv = NULL;
458
Yuanyuan Liu99131f02017-07-06 13:58:33 -0700459 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700460 if (!priv)
461 return -ENOMEM;
462
463 priv->cc_source = CNSS_UTILS_SOURCE_CORE;
464
465 mutex_init(&priv->unsafe_channel_list_lock);
466 spin_lock_init(&priv->dfs_nol_info_lock);
Sameer Thalappild7ab5882017-11-30 17:31:16 -0800467 cnss_utils_debugfs_create(priv);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700468 cnss_utils_priv = priv;
469
470 return 0;
471}
472
473static void __exit cnss_utils_exit(void)
474{
475 kfree(cnss_utils_priv);
476 cnss_utils_priv = NULL;
477}
478
479module_init(cnss_utils_init);
480module_exit(cnss_utils_exit);
481
482MODULE_LICENSE("GPL v2");
483MODULE_DESCRIPTION(DEVICE "CNSS Utilities Driver");