blob: 49551309c42c83e9903c516b7dd448c296b72f96 [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>
16#include <linux/slab.h>
17#include <linux/etherdevice.h>
18#include <net/cnss_utils.h>
19
20#define CNSS_MAX_CH_NUM 45
21struct cnss_unsafe_channel_list {
22 u16 unsafe_ch_count;
23 u16 unsafe_ch_list[CNSS_MAX_CH_NUM];
24};
25
26struct cnss_dfs_nol_info {
27 void *dfs_nol_info;
28 u16 dfs_nol_info_len;
29};
30
31#define MAX_NO_OF_MAC_ADDR 4
32struct cnss_wlan_mac_addr {
33 u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
34 u32 no_of_mac_addr_set;
35};
36
Sameer Thalappil1a15d712017-11-08 17:08:48 -080037enum mac_type {
38 CNSS_MAC_PROVISIONED,
39 CNSS_MAC_DERIVED,
40};
41
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070042static struct cnss_utils_priv {
43 struct cnss_unsafe_channel_list unsafe_channel_list;
44 struct cnss_dfs_nol_info dfs_nol_info;
45 /* generic mutex for unsafe channel */
46 struct mutex unsafe_channel_list_lock;
47 /* generic spin-lock for dfs_nol info */
48 spinlock_t dfs_nol_info_lock;
49 int driver_load_cnt;
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070050 struct cnss_wlan_mac_addr wlan_mac_addr;
Sameer Thalappil1a15d712017-11-08 17:08:48 -080051 struct cnss_wlan_mac_addr wlan_der_mac_addr;
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -070052 enum cnss_utils_cc_src cc_source;
53} *cnss_utils_priv;
54
55int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
56 u16 *unsafe_ch_list, u16 ch_count)
57{
58 struct cnss_utils_priv *priv = cnss_utils_priv;
59
60 if (!priv)
61 return -EINVAL;
62
63 mutex_lock(&priv->unsafe_channel_list_lock);
64 if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) {
65 mutex_unlock(&priv->unsafe_channel_list_lock);
66 return -EINVAL;
67 }
68
69 priv->unsafe_channel_list.unsafe_ch_count = ch_count;
70
71 if (ch_count == 0)
72 goto end;
73
74 memcpy(priv->unsafe_channel_list.unsafe_ch_list,
75 unsafe_ch_list, ch_count * sizeof(u16));
76
77end:
78 mutex_unlock(&priv->unsafe_channel_list_lock);
79
80 return 0;
81}
82EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
83
84int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
85 u16 *unsafe_ch_list,
86 u16 *ch_count, u16 buf_len)
87{
88 struct cnss_utils_priv *priv = cnss_utils_priv;
89
90 if (!priv)
91 return -EINVAL;
92
93 mutex_lock(&priv->unsafe_channel_list_lock);
94 if (!unsafe_ch_list || !ch_count) {
95 mutex_unlock(&priv->unsafe_channel_list_lock);
96 return -EINVAL;
97 }
98
99 if (buf_len <
100 (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
101 mutex_unlock(&priv->unsafe_channel_list_lock);
102 return -ENOMEM;
103 }
104
105 *ch_count = priv->unsafe_channel_list.unsafe_ch_count;
106 memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
107 priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
108 mutex_unlock(&priv->unsafe_channel_list_lock);
109
110 return 0;
111}
112EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
113
114int cnss_utils_wlan_set_dfs_nol(struct device *dev,
115 const void *info, u16 info_len)
116{
117 void *temp;
118 void *old_nol_info;
119 struct cnss_dfs_nol_info *dfs_info;
120 struct cnss_utils_priv *priv = cnss_utils_priv;
121
122 if (!priv)
123 return -EINVAL;
124
125 if (!info || !info_len)
126 return -EINVAL;
127
128 temp = kmalloc(info_len, GFP_ATOMIC);
129 if (!temp)
130 return -ENOMEM;
131
132 memcpy(temp, info, info_len);
133 spin_lock_bh(&priv->dfs_nol_info_lock);
134 dfs_info = &priv->dfs_nol_info;
135 old_nol_info = dfs_info->dfs_nol_info;
136 dfs_info->dfs_nol_info = temp;
137 dfs_info->dfs_nol_info_len = info_len;
138 spin_unlock_bh(&priv->dfs_nol_info_lock);
139 kfree(old_nol_info);
140
141 return 0;
142}
143EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
144
145int cnss_utils_wlan_get_dfs_nol(struct device *dev,
146 void *info, u16 info_len)
147{
148 int len;
149 struct cnss_dfs_nol_info *dfs_info;
150 struct cnss_utils_priv *priv = cnss_utils_priv;
151
152 if (!priv)
153 return -EINVAL;
154
155 if (!info || !info_len)
156 return -EINVAL;
157
158 spin_lock_bh(&priv->dfs_nol_info_lock);
159
160 dfs_info = &priv->dfs_nol_info;
161 if (!dfs_info->dfs_nol_info ||
162 dfs_info->dfs_nol_info_len == 0) {
163 spin_unlock_bh(&priv->dfs_nol_info_lock);
164 return -ENOENT;
165 }
166
167 len = min(info_len, dfs_info->dfs_nol_info_len);
168 memcpy(info, dfs_info->dfs_nol_info, len);
169 spin_unlock_bh(&priv->dfs_nol_info_lock);
170
171 return len;
172}
173EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
174
175void cnss_utils_increment_driver_load_cnt(struct device *dev)
176{
177 struct cnss_utils_priv *priv = cnss_utils_priv;
178
179 if (!priv)
180 return;
181
182 ++(priv->driver_load_cnt);
183}
184EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
185
186int cnss_utils_get_driver_load_cnt(struct device *dev)
187{
188 struct cnss_utils_priv *priv = cnss_utils_priv;
189
190 if (!priv)
191 return -EINVAL;
192
193 return priv->driver_load_cnt;
194}
195EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
196
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800197static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len,
198 enum mac_type type)
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700199{
200 struct cnss_utils_priv *priv = cnss_utils_priv;
201 u32 no_of_mac_addr;
202 struct cnss_wlan_mac_addr *addr = NULL;
203 int iter;
204 u8 *temp = NULL;
205
206 if (!priv)
207 return -EINVAL;
208
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700209 if (len == 0 || (len % ETH_ALEN) != 0) {
210 pr_err("Invalid length %d\n", len);
211 return -EINVAL;
212 }
213
214 no_of_mac_addr = len / ETH_ALEN;
215 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
216 pr_err("Exceed maximum supported MAC address %u %u\n",
217 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
218 return -EINVAL;
219 }
220
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800221 if (type == CNSS_MAC_PROVISIONED)
222 addr = &priv->wlan_mac_addr;
223 else
224 addr = &priv->wlan_der_mac_addr;
225
226 if (addr->no_of_mac_addr_set) {
227 pr_err("WLAN MAC address is already set, num %d type %d\n",
228 addr->no_of_mac_addr_set, type);
229 return 0;
230 }
231
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700232 addr->no_of_mac_addr_set = no_of_mac_addr;
233 temp = &addr->mac_addr[0][0];
234
235 for (iter = 0; iter < no_of_mac_addr;
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800236 ++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) {
237 ether_addr_copy(temp, mac_list);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700238 pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
239 temp[0], temp[1], temp[2],
240 temp[3], temp[4], temp[5]);
241 }
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700242 return 0;
243}
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800244
245int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len)
246{
247 return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED);
248}
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700249EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
250
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800251int cnss_utils_set_wlan_derived_mac_address(
252 const u8 *mac_list, const uint32_t len)
253{
254 return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED);
255}
256EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address);
257
258static u8 *get_wlan_mac_address(struct device *dev,
259 u32 *num, enum mac_type type)
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700260{
261 struct cnss_utils_priv *priv = cnss_utils_priv;
262 struct cnss_wlan_mac_addr *addr = NULL;
263
264 if (!priv)
265 goto out;
266
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800267 if (type == CNSS_MAC_PROVISIONED)
268 addr = &priv->wlan_mac_addr;
269 else
270 addr = &priv->wlan_der_mac_addr;
271
272 if (!addr->no_of_mac_addr_set) {
273 pr_err("WLAN MAC address is not set, type %d\n", type);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700274 goto out;
275 }
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700276 *num = addr->no_of_mac_addr_set;
277 return &addr->mac_addr[0][0];
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800278
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700279out:
280 *num = 0;
281 return NULL;
282}
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800283
284u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
285{
286 return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED);
287}
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700288EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
289
Sameer Thalappil1a15d712017-11-08 17:08:48 -0800290u8 *cnss_utils_get_wlan_derived_mac_address(
291 struct device *dev, uint32_t *num)
292{
293 return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED);
294}
295EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address);
296
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700297void cnss_utils_set_cc_source(struct device *dev,
298 enum cnss_utils_cc_src cc_source)
299{
300 struct cnss_utils_priv *priv = cnss_utils_priv;
301
302 if (!priv)
303 return;
304
305 priv->cc_source = cc_source;
306}
307EXPORT_SYMBOL(cnss_utils_set_cc_source);
308
309enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
310{
311 struct cnss_utils_priv *priv = cnss_utils_priv;
312
313 if (!priv)
314 return -EINVAL;
315
316 return priv->cc_source;
317}
318EXPORT_SYMBOL(cnss_utils_get_cc_source);
319
320static int __init cnss_utils_init(void)
321{
322 struct cnss_utils_priv *priv = NULL;
323
Yuanyuan Liu99131f02017-07-06 13:58:33 -0700324 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700325 if (!priv)
326 return -ENOMEM;
327
328 priv->cc_source = CNSS_UTILS_SOURCE_CORE;
329
330 mutex_init(&priv->unsafe_channel_list_lock);
331 spin_lock_init(&priv->dfs_nol_info_lock);
332
333 cnss_utils_priv = priv;
334
335 return 0;
336}
337
338static void __exit cnss_utils_exit(void)
339{
340 kfree(cnss_utils_priv);
341 cnss_utils_priv = NULL;
342}
343
344module_init(cnss_utils_init);
345module_exit(cnss_utils_exit);
346
347MODULE_LICENSE("GPL v2");
348MODULE_DESCRIPTION(DEVICE "CNSS Utilities Driver");