Commit | Line | Data |
---|---|---|
15dbaac0 JC |
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)->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, "0x%x\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)->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, "%x", &datum); | |
67 | if (ret != 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, "0x%x\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)->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, "%x", &datum); | |
110 | if (ret != 1) | |
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 | ||
b679aeb3 JC |
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, "0x%x\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)->priv; | |
156 | struct cmd_ds_mesh_config cmd; | |
157 | uint16_t datum; | |
158 | int ret; | |
159 | ||
160 | memset(&cmd, 0, sizeof(cmd)); | |
161 | ret = sscanf(buf, "%hx", &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 | ||
15dbaac0 JC |
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 > IW_ESSID_MAX_SIZE) { | |
191 | printk(KERN_ERR "Inconsistent mesh ID length"); | |
192 | defs.meshie.val.mesh_id_len = IW_ESSID_MAX_SIZE; | |
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)->priv; | |
214 | int len; | |
215 | int ret; | |
216 | ||
217 | if (count < 2 || count > IW_ESSID_MAX_SIZE + 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->hdr.len = sizeof(struct mrvl_meshie_val) - IW_ESSID_MAX_SIZE + 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)->priv; | |
273 | uint32_t datum; | |
274 | int ret; | |
275 | ||
276 | memset(&cmd, 0, sizeof(cmd)); | |
277 | ret = sscanf(buf, "%x", &datum); | |
278 | if (ret != 1) | |
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)->priv; | |
327 | uint32_t datum; | |
328 | int ret; | |
329 | ||
330 | memset(&cmd, 0, sizeof(cmd)); | |
331 | ret = sscanf(buf, "%x", &datum); | |
332 | if (ret != 1) | |
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)->priv; | |
381 | uint32_t datum; | |
382 | int ret; | |
383 | ||
384 | memset(&cmd, 0, sizeof(cmd)); | |
385 | ret = sscanf(buf, "%x", &datum); | |
386 | if (ret != 1) | |
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); | |
b679aeb3 | 411 | static DEVICE_ATTR(channel, 0644, channel_get, channel_set); |
15dbaac0 JC |
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, | |
b679aeb3 | 420 | &dev_attr_channel.attr, |
15dbaac0 JC |
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 | } |