blob: d73846efbc4c938a43b26bfbebbdfe874c0b846e [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
37static 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 bool is_wlan_mac_set;
46 struct cnss_wlan_mac_addr wlan_mac_addr;
47 enum cnss_utils_cc_src cc_source;
48} *cnss_utils_priv;
49
50int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
51 u16 *unsafe_ch_list, u16 ch_count)
52{
53 struct cnss_utils_priv *priv = cnss_utils_priv;
54
55 if (!priv)
56 return -EINVAL;
57
58 mutex_lock(&priv->unsafe_channel_list_lock);
59 if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) {
60 mutex_unlock(&priv->unsafe_channel_list_lock);
61 return -EINVAL;
62 }
63
64 priv->unsafe_channel_list.unsafe_ch_count = ch_count;
65
66 if (ch_count == 0)
67 goto end;
68
69 memcpy(priv->unsafe_channel_list.unsafe_ch_list,
70 unsafe_ch_list, ch_count * sizeof(u16));
71
72end:
73 mutex_unlock(&priv->unsafe_channel_list_lock);
74
75 return 0;
76}
77EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
78
79int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
80 u16 *unsafe_ch_list,
81 u16 *ch_count, u16 buf_len)
82{
83 struct cnss_utils_priv *priv = cnss_utils_priv;
84
85 if (!priv)
86 return -EINVAL;
87
88 mutex_lock(&priv->unsafe_channel_list_lock);
89 if (!unsafe_ch_list || !ch_count) {
90 mutex_unlock(&priv->unsafe_channel_list_lock);
91 return -EINVAL;
92 }
93
94 if (buf_len <
95 (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
96 mutex_unlock(&priv->unsafe_channel_list_lock);
97 return -ENOMEM;
98 }
99
100 *ch_count = priv->unsafe_channel_list.unsafe_ch_count;
101 memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
102 priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
103 mutex_unlock(&priv->unsafe_channel_list_lock);
104
105 return 0;
106}
107EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
108
109int cnss_utils_wlan_set_dfs_nol(struct device *dev,
110 const void *info, u16 info_len)
111{
112 void *temp;
113 void *old_nol_info;
114 struct cnss_dfs_nol_info *dfs_info;
115 struct cnss_utils_priv *priv = cnss_utils_priv;
116
117 if (!priv)
118 return -EINVAL;
119
120 if (!info || !info_len)
121 return -EINVAL;
122
123 temp = kmalloc(info_len, GFP_ATOMIC);
124 if (!temp)
125 return -ENOMEM;
126
127 memcpy(temp, info, info_len);
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}
138EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
139
140int 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}
168EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
169
170void 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}
179EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
180
181int 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}
190EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
191
192int cnss_utils_set_wlan_mac_address(const u8 *in, const uint32_t len)
193{
194 struct cnss_utils_priv *priv = cnss_utils_priv;
195 u32 no_of_mac_addr;
196 struct cnss_wlan_mac_addr *addr = NULL;
197 int iter;
198 u8 *temp = NULL;
199
200 if (!priv)
201 return -EINVAL;
202
203 if (priv->is_wlan_mac_set) {
204 pr_debug("WLAN MAC address is already set\n");
205 return 0;
206 }
207
208 if (len == 0 || (len % ETH_ALEN) != 0) {
209 pr_err("Invalid length %d\n", len);
210 return -EINVAL;
211 }
212
213 no_of_mac_addr = len / ETH_ALEN;
214 if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
215 pr_err("Exceed maximum supported MAC address %u %u\n",
216 MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
217 return -EINVAL;
218 }
219
220 priv->is_wlan_mac_set = true;
221 addr = &priv->wlan_mac_addr;
222 addr->no_of_mac_addr_set = no_of_mac_addr;
223 temp = &addr->mac_addr[0][0];
224
225 for (iter = 0; iter < no_of_mac_addr;
226 ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
227 ether_addr_copy(temp, in);
228 pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
229 temp[0], temp[1], temp[2],
230 temp[3], temp[4], temp[5]);
231 }
232
233 return 0;
234}
235EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
236
237u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
238{
239 struct cnss_utils_priv *priv = cnss_utils_priv;
240 struct cnss_wlan_mac_addr *addr = NULL;
241
242 if (!priv)
243 goto out;
244
245 if (!priv->is_wlan_mac_set) {
246 pr_debug("WLAN MAC address is not set\n");
247 goto out;
248 }
249
250 addr = &priv->wlan_mac_addr;
251 *num = addr->no_of_mac_addr_set;
252 return &addr->mac_addr[0][0];
253out:
254 *num = 0;
255 return NULL;
256}
257EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
258
259void cnss_utils_set_cc_source(struct device *dev,
260 enum cnss_utils_cc_src cc_source)
261{
262 struct cnss_utils_priv *priv = cnss_utils_priv;
263
264 if (!priv)
265 return;
266
267 priv->cc_source = cc_source;
268}
269EXPORT_SYMBOL(cnss_utils_set_cc_source);
270
271enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
272{
273 struct cnss_utils_priv *priv = cnss_utils_priv;
274
275 if (!priv)
276 return -EINVAL;
277
278 return priv->cc_source;
279}
280EXPORT_SYMBOL(cnss_utils_get_cc_source);
281
282static int __init cnss_utils_init(void)
283{
284 struct cnss_utils_priv *priv = NULL;
285
Yuanyuan Liu99131f02017-07-06 13:58:33 -0700286 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
Yuanyuan Liu13e7cdf2017-06-13 11:48:00 -0700287 if (!priv)
288 return -ENOMEM;
289
290 priv->cc_source = CNSS_UTILS_SOURCE_CORE;
291
292 mutex_init(&priv->unsafe_channel_list_lock);
293 spin_lock_init(&priv->dfs_nol_info_lock);
294
295 cnss_utils_priv = priv;
296
297 return 0;
298}
299
300static void __exit cnss_utils_exit(void)
301{
302 kfree(cnss_utils_priv);
303 cnss_utils_priv = NULL;
304}
305
306module_init(cnss_utils_init);
307module_exit(cnss_utils_exit);
308
309MODULE_LICENSE("GPL v2");
310MODULE_DESCRIPTION(DEVICE "CNSS Utilities Driver");