diff options
Diffstat (limited to 'drivers/net/wireless/libertas/mesh.c')
-rw-r--r-- | drivers/net/wireless/libertas/mesh.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c new file mode 100644 index 000000000000..871f914a75fc --- /dev/null +++ b/drivers/net/wireless/libertas/mesh.c | |||
@@ -0,0 +1,453 @@ | |||
1 | #include <linux/moduleparam.h> | ||
2 | #include <linux/delay.h> | ||
3 | #include <linux/etherdevice.h> | ||
4 | #include <linux/netdevice.h> | ||
5 | #include <linux/if_arp.h> | ||
6 | #include <linux/kthread.h> | ||
7 | #include <linux/kfifo.h> | ||
8 | |||
9 | #include "host.h" | ||
10 | #include "decl.h" | ||
11 | #include "dev.h" | ||
12 | #include "wext.h" | ||
13 | #include "debugfs.h" | ||
14 | #include "scan.h" | ||
15 | #include "assoc.h" | ||
16 | #include "cmd.h" | ||
17 | |||
18 | static int mesh_get_default_parameters(struct device *dev, | ||
19 | struct mrvl_mesh_defaults *defs) | ||
20 | { | ||
21 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
22 | struct cmd_ds_mesh_config cmd; | ||
23 | int ret; | ||
24 | |||
25 | memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); | ||
26 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, | ||
27 | CMD_TYPE_MESH_GET_DEFAULTS); | ||
28 | |||
29 | if (ret) | ||
30 | return -EOPNOTSUPP; | ||
31 | |||
32 | memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * @brief Get function for sysfs attribute bootflag | ||
39 | */ | ||
40 | static ssize_t bootflag_get(struct device *dev, | ||
41 | struct device_attribute *attr, char *buf) | ||
42 | { | ||
43 | struct mrvl_mesh_defaults defs; | ||
44 | int ret; | ||
45 | |||
46 | ret = mesh_get_default_parameters(dev, &defs); | ||
47 | |||
48 | if (ret) | ||
49 | return ret; | ||
50 | |||
51 | return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * @brief Set function for sysfs attribute bootflag | ||
56 | */ | ||
57 | static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, | ||
58 | const char *buf, size_t count) | ||
59 | { | ||
60 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
61 | struct cmd_ds_mesh_config cmd; | ||
62 | uint32_t datum; | ||
63 | int ret; | ||
64 | |||
65 | memset(&cmd, 0, sizeof(cmd)); | ||
66 | ret = sscanf(buf, "%d", &datum); | ||
67 | if ((ret != 1) || (datum > 1)) | ||
68 | return -EINVAL; | ||
69 | |||
70 | *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); | ||
71 | cmd.length = cpu_to_le16(sizeof(uint32_t)); | ||
72 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
73 | CMD_TYPE_MESH_SET_BOOTFLAG); | ||
74 | if (ret) | ||
75 | return ret; | ||
76 | |||
77 | return strlen(buf); | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * @brief Get function for sysfs attribute boottime | ||
82 | */ | ||
83 | static ssize_t boottime_get(struct device *dev, | ||
84 | struct device_attribute *attr, char *buf) | ||
85 | { | ||
86 | struct mrvl_mesh_defaults defs; | ||
87 | int ret; | ||
88 | |||
89 | ret = mesh_get_default_parameters(dev, &defs); | ||
90 | |||
91 | if (ret) | ||
92 | return ret; | ||
93 | |||
94 | return snprintf(buf, 12, "%d\n", defs.boottime); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * @brief Set function for sysfs attribute boottime | ||
99 | */ | ||
100 | static ssize_t boottime_set(struct device *dev, | ||
101 | struct device_attribute *attr, const char *buf, size_t count) | ||
102 | { | ||
103 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
104 | struct cmd_ds_mesh_config cmd; | ||
105 | uint32_t datum; | ||
106 | int ret; | ||
107 | |||
108 | memset(&cmd, 0, sizeof(cmd)); | ||
109 | ret = sscanf(buf, "%d", &datum); | ||
110 | if ((ret != 1) || (datum > 255)) | ||
111 | return -EINVAL; | ||
112 | |||
113 | /* A too small boot time will result in the device booting into | ||
114 | * standalone (no-host) mode before the host can take control of it, | ||
115 | * so the change will be hard to revert. This may be a desired | ||
116 | * feature (e.g to configure a very fast boot time for devices that | ||
117 | * will not be attached to a host), but dangerous. So I'm enforcing a | ||
118 | * lower limit of 20 seconds: remove and recompile the driver if this | ||
119 | * does not work for you. | ||
120 | */ | ||
121 | datum = (datum < 20) ? 20 : datum; | ||
122 | cmd.data[0] = datum; | ||
123 | cmd.length = cpu_to_le16(sizeof(uint8_t)); | ||
124 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
125 | CMD_TYPE_MESH_SET_BOOTTIME); | ||
126 | if (ret) | ||
127 | return ret; | ||
128 | |||
129 | return strlen(buf); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * @brief Get function for sysfs attribute channel | ||
134 | */ | ||
135 | static ssize_t channel_get(struct device *dev, | ||
136 | struct device_attribute *attr, char *buf) | ||
137 | { | ||
138 | struct mrvl_mesh_defaults defs; | ||
139 | int ret; | ||
140 | |||
141 | ret = mesh_get_default_parameters(dev, &defs); | ||
142 | |||
143 | if (ret) | ||
144 | return ret; | ||
145 | |||
146 | return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * @brief Set function for sysfs attribute channel | ||
151 | */ | ||
152 | static ssize_t channel_set(struct device *dev, struct device_attribute *attr, | ||
153 | const char *buf, size_t count) | ||
154 | { | ||
155 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
156 | struct cmd_ds_mesh_config cmd; | ||
157 | uint32_t datum; | ||
158 | int ret; | ||
159 | |||
160 | memset(&cmd, 0, sizeof(cmd)); | ||
161 | ret = sscanf(buf, "%d", &datum); | ||
162 | if (ret != 1 || datum < 1 || datum > 11) | ||
163 | return -EINVAL; | ||
164 | |||
165 | *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); | ||
166 | cmd.length = cpu_to_le16(sizeof(uint16_t)); | ||
167 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
168 | CMD_TYPE_MESH_SET_DEF_CHANNEL); | ||
169 | if (ret) | ||
170 | return ret; | ||
171 | |||
172 | return strlen(buf); | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * @brief Get function for sysfs attribute mesh_id | ||
177 | */ | ||
178 | static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, | ||
179 | char *buf) | ||
180 | { | ||
181 | struct mrvl_mesh_defaults defs; | ||
182 | int maxlen; | ||
183 | int ret; | ||
184 | |||
185 | ret = mesh_get_default_parameters(dev, &defs); | ||
186 | |||
187 | if (ret) | ||
188 | return ret; | ||
189 | |||
190 | if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { | ||
191 | lbs_pr_err("inconsistent mesh ID length"); | ||
192 | defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; | ||
193 | } | ||
194 | |||
195 | /* SSID not null terminated: reserve room for \0 + \n */ | ||
196 | maxlen = defs.meshie.val.mesh_id_len + 2; | ||
197 | maxlen = (PAGE_SIZE > maxlen) ? maxlen : PAGE_SIZE; | ||
198 | |||
199 | defs.meshie.val.mesh_id[defs.meshie.val.mesh_id_len] = '\0'; | ||
200 | |||
201 | return snprintf(buf, maxlen, "%s\n", defs.meshie.val.mesh_id); | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * @brief Set function for sysfs attribute mesh_id | ||
206 | */ | ||
207 | static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, | ||
208 | const char *buf, size_t count) | ||
209 | { | ||
210 | struct cmd_ds_mesh_config cmd; | ||
211 | struct mrvl_mesh_defaults defs; | ||
212 | struct mrvl_meshie *ie; | ||
213 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
214 | int len; | ||
215 | int ret; | ||
216 | |||
217 | if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) | ||
218 | return -EINVAL; | ||
219 | |||
220 | memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); | ||
221 | ie = (struct mrvl_meshie *) &cmd.data[0]; | ||
222 | |||
223 | /* fetch all other Information Element parameters */ | ||
224 | ret = mesh_get_default_parameters(dev, &defs); | ||
225 | |||
226 | cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); | ||
227 | |||
228 | /* transfer IE elements */ | ||
229 | memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); | ||
230 | |||
231 | len = count - 1; | ||
232 | memcpy(ie->val.mesh_id, buf, len); | ||
233 | /* SSID len */ | ||
234 | ie->val.mesh_id_len = len; | ||
235 | /* IE len */ | ||
236 | ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; | ||
237 | |||
238 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
239 | CMD_TYPE_MESH_SET_MESH_IE); | ||
240 | if (ret) | ||
241 | return ret; | ||
242 | |||
243 | return strlen(buf); | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * @brief Get function for sysfs attribute protocol_id | ||
248 | */ | ||
249 | static ssize_t protocol_id_get(struct device *dev, | ||
250 | struct device_attribute *attr, char *buf) | ||
251 | { | ||
252 | struct mrvl_mesh_defaults defs; | ||
253 | int ret; | ||
254 | |||
255 | ret = mesh_get_default_parameters(dev, &defs); | ||
256 | |||
257 | if (ret) | ||
258 | return ret; | ||
259 | |||
260 | return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * @brief Set function for sysfs attribute protocol_id | ||
265 | */ | ||
266 | static ssize_t protocol_id_set(struct device *dev, | ||
267 | struct device_attribute *attr, const char *buf, size_t count) | ||
268 | { | ||
269 | struct cmd_ds_mesh_config cmd; | ||
270 | struct mrvl_mesh_defaults defs; | ||
271 | struct mrvl_meshie *ie; | ||
272 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
273 | uint32_t datum; | ||
274 | int ret; | ||
275 | |||
276 | memset(&cmd, 0, sizeof(cmd)); | ||
277 | ret = sscanf(buf, "%d", &datum); | ||
278 | if ((ret != 1) || (datum > 255)) | ||
279 | return -EINVAL; | ||
280 | |||
281 | /* fetch all other Information Element parameters */ | ||
282 | ret = mesh_get_default_parameters(dev, &defs); | ||
283 | |||
284 | cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); | ||
285 | |||
286 | /* transfer IE elements */ | ||
287 | ie = (struct mrvl_meshie *) &cmd.data[0]; | ||
288 | memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); | ||
289 | /* update protocol id */ | ||
290 | ie->val.active_protocol_id = datum; | ||
291 | |||
292 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
293 | CMD_TYPE_MESH_SET_MESH_IE); | ||
294 | if (ret) | ||
295 | return ret; | ||
296 | |||
297 | return strlen(buf); | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * @brief Get function for sysfs attribute metric_id | ||
302 | */ | ||
303 | static ssize_t metric_id_get(struct device *dev, | ||
304 | struct device_attribute *attr, char *buf) | ||
305 | { | ||
306 | struct mrvl_mesh_defaults defs; | ||
307 | int ret; | ||
308 | |||
309 | ret = mesh_get_default_parameters(dev, &defs); | ||
310 | |||
311 | if (ret) | ||
312 | return ret; | ||
313 | |||
314 | return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * @brief Set function for sysfs attribute metric_id | ||
319 | */ | ||
320 | static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, | ||
321 | const char *buf, size_t count) | ||
322 | { | ||
323 | struct cmd_ds_mesh_config cmd; | ||
324 | struct mrvl_mesh_defaults defs; | ||
325 | struct mrvl_meshie *ie; | ||
326 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
327 | uint32_t datum; | ||
328 | int ret; | ||
329 | |||
330 | memset(&cmd, 0, sizeof(cmd)); | ||
331 | ret = sscanf(buf, "%d", &datum); | ||
332 | if ((ret != 1) || (datum > 255)) | ||
333 | return -EINVAL; | ||
334 | |||
335 | /* fetch all other Information Element parameters */ | ||
336 | ret = mesh_get_default_parameters(dev, &defs); | ||
337 | |||
338 | cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); | ||
339 | |||
340 | /* transfer IE elements */ | ||
341 | ie = (struct mrvl_meshie *) &cmd.data[0]; | ||
342 | memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); | ||
343 | /* update metric id */ | ||
344 | ie->val.active_metric_id = datum; | ||
345 | |||
346 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
347 | CMD_TYPE_MESH_SET_MESH_IE); | ||
348 | if (ret) | ||
349 | return ret; | ||
350 | |||
351 | return strlen(buf); | ||
352 | } | ||
353 | |||
354 | /** | ||
355 | * @brief Get function for sysfs attribute capability | ||
356 | */ | ||
357 | static ssize_t capability_get(struct device *dev, | ||
358 | struct device_attribute *attr, char *buf) | ||
359 | { | ||
360 | struct mrvl_mesh_defaults defs; | ||
361 | int ret; | ||
362 | |||
363 | ret = mesh_get_default_parameters(dev, &defs); | ||
364 | |||
365 | if (ret) | ||
366 | return ret; | ||
367 | |||
368 | return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * @brief Set function for sysfs attribute capability | ||
373 | */ | ||
374 | static ssize_t capability_set(struct device *dev, struct device_attribute *attr, | ||
375 | const char *buf, size_t count) | ||
376 | { | ||
377 | struct cmd_ds_mesh_config cmd; | ||
378 | struct mrvl_mesh_defaults defs; | ||
379 | struct mrvl_meshie *ie; | ||
380 | struct lbs_private *priv = to_net_dev(dev)->ml_priv; | ||
381 | uint32_t datum; | ||
382 | int ret; | ||
383 | |||
384 | memset(&cmd, 0, sizeof(cmd)); | ||
385 | ret = sscanf(buf, "%d", &datum); | ||
386 | if ((ret != 1) || (datum > 255)) | ||
387 | return -EINVAL; | ||
388 | |||
389 | /* fetch all other Information Element parameters */ | ||
390 | ret = mesh_get_default_parameters(dev, &defs); | ||
391 | |||
392 | cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); | ||
393 | |||
394 | /* transfer IE elements */ | ||
395 | ie = (struct mrvl_meshie *) &cmd.data[0]; | ||
396 | memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); | ||
397 | /* update value */ | ||
398 | ie->val.mesh_capability = datum; | ||
399 | |||
400 | ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, | ||
401 | CMD_TYPE_MESH_SET_MESH_IE); | ||
402 | if (ret) | ||
403 | return ret; | ||
404 | |||
405 | return strlen(buf); | ||
406 | } | ||
407 | |||
408 | |||
409 | static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); | ||
410 | static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); | ||
411 | static DEVICE_ATTR(channel, 0644, channel_get, channel_set); | ||
412 | static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); | ||
413 | static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); | ||
414 | static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); | ||
415 | static DEVICE_ATTR(capability, 0644, capability_get, capability_set); | ||
416 | |||
417 | static struct attribute *boot_opts_attrs[] = { | ||
418 | &dev_attr_bootflag.attr, | ||
419 | &dev_attr_boottime.attr, | ||
420 | &dev_attr_channel.attr, | ||
421 | NULL | ||
422 | }; | ||
423 | |||
424 | static struct attribute_group boot_opts_group = { | ||
425 | .name = "boot_options", | ||
426 | .attrs = boot_opts_attrs, | ||
427 | }; | ||
428 | |||
429 | static struct attribute *mesh_ie_attrs[] = { | ||
430 | &dev_attr_mesh_id.attr, | ||
431 | &dev_attr_protocol_id.attr, | ||
432 | &dev_attr_metric_id.attr, | ||
433 | &dev_attr_capability.attr, | ||
434 | NULL | ||
435 | }; | ||
436 | |||
437 | static struct attribute_group mesh_ie_group = { | ||
438 | .name = "mesh_ie", | ||
439 | .attrs = mesh_ie_attrs, | ||
440 | }; | ||
441 | |||
442 | void lbs_persist_config_init(struct net_device *dev) | ||
443 | { | ||
444 | int ret; | ||
445 | ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); | ||
446 | ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); | ||
447 | } | ||
448 | |||
449 | void lbs_persist_config_remove(struct net_device *dev) | ||
450 | { | ||
451 | sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); | ||
452 | sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); | ||
453 | } | ||