Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
[deliverable/linux.git] / drivers / net / wireless / libertas / mesh.c
CommitLineData
0e4e06ae
JP
1#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2
15dbaac0
JC
3#include <linux/delay.h>
4#include <linux/etherdevice.h>
a6b7a407 5#include <linux/hardirq.h>
15dbaac0 6#include <linux/netdevice.h>
1f044931 7#include <linux/if_ether.h>
15dbaac0
JC
8#include <linux/if_arp.h>
9#include <linux/kthread.h>
10#include <linux/kfifo.h>
e86dc1ca 11#include <net/cfg80211.h>
15dbaac0 12
e0e42da3 13#include "mesh.h"
15dbaac0 14#include "decl.h"
15dbaac0
JC
15#include "cmd.h"
16
e0e42da3 17
3db4f989
DD
18static int lbs_add_mesh(struct lbs_private *priv);
19
20/***************************************************************************
21 * Mesh command handling
22 */
23
24static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
25 struct cmd_ds_mesh_access *cmd)
26{
27 int ret;
28
29 lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
30
31 cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
32 cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
33 cmd->hdr.result = 0;
34
35 cmd->action = cpu_to_le16(cmd_action);
36
37 ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
38
39 lbs_deb_leave(LBS_DEB_CMD);
40 return ret;
41}
42
43static int __lbs_mesh_config_send(struct lbs_private *priv,
44 struct cmd_ds_mesh_config *cmd,
45 uint16_t action, uint16_t type)
46{
47 int ret;
48 u16 command = CMD_MESH_CONFIG_OLD;
49
50 lbs_deb_enter(LBS_DEB_CMD);
51
52 /*
53 * Command id is 0xac for v10 FW along with mesh interface
54 * id in bits 14-13-12.
55 */
56 if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
57 command = CMD_MESH_CONFIG |
58 (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
59
60 cmd->hdr.command = cpu_to_le16(command);
61 cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
62 cmd->hdr.result = 0;
63
64 cmd->type = cpu_to_le16(type);
65 cmd->action = cpu_to_le16(action);
66
67 ret = lbs_cmd_with_response(priv, command, cmd);
68
69 lbs_deb_leave(LBS_DEB_CMD);
70 return ret;
71}
72
73static int lbs_mesh_config_send(struct lbs_private *priv,
74 struct cmd_ds_mesh_config *cmd,
75 uint16_t action, uint16_t type)
76{
77 int ret;
78
79 if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
80 return -EOPNOTSUPP;
81
82 ret = __lbs_mesh_config_send(priv, cmd, action, type);
83 return ret;
84}
85
86/* This function is the CMD_MESH_CONFIG legacy function. It only handles the
87 * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG
88 * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
89 * lbs_mesh_config_send.
90 */
91static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
92 uint16_t chan)
93{
94 struct cmd_ds_mesh_config cmd;
95 struct mrvl_meshie *ie;
96 DECLARE_SSID_BUF(ssid);
97
98 memset(&cmd, 0, sizeof(cmd));
99 cmd.channel = cpu_to_le16(chan);
100 ie = (struct mrvl_meshie *)cmd.data;
101
102 switch (action) {
103 case CMD_ACT_MESH_CONFIG_START:
04b2312a 104 ie->id = WLAN_EID_VENDOR_SPECIFIC;
3db4f989
DD
105 ie->val.oui[0] = 0x00;
106 ie->val.oui[1] = 0x50;
107 ie->val.oui[2] = 0x43;
108 ie->val.type = MARVELL_MESH_IE_TYPE;
109 ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
110 ie->val.version = MARVELL_MESH_IE_VERSION;
111 ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
112 ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
113 ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
114 ie->val.mesh_id_len = priv->mesh_ssid_len;
115 memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
116 ie->len = sizeof(struct mrvl_meshie_val) -
117 IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len;
118 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
119 break;
120 case CMD_ACT_MESH_CONFIG_STOP:
121 break;
122 default:
123 return -1;
124 }
125 lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
126 action, priv->mesh_tlv, chan,
127 print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
128
129 return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
130}
131
49fee692
DD
132int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
133{
e8c9bd5b 134 priv->mesh_channel = channel;
49fee692
DD
135 return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
136}
137
138static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
139{
e8c9bd5b 140 return priv->mesh_channel ?: 1;
49fee692 141}
3db4f989 142
e0e42da3
HS
143/***************************************************************************
144 * Mesh sysfs support
145 */
146
8973a6e7 147/*
e0e42da3
HS
148 * Attributes exported through sysfs
149 */
150
151/**
8973a6e7
RD
152 * lbs_anycast_get - Get function for sysfs attribute anycast_mask
153 * @dev: the &struct device
154 * @attr: device attributes
155 * @buf: buffer where data will be returned
e0e42da3
HS
156 */
157static ssize_t lbs_anycast_get(struct device *dev,
158 struct device_attribute *attr, char * buf)
159{
160 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
161 struct cmd_ds_mesh_access mesh_access;
162 int ret;
163
164 memset(&mesh_access, 0, sizeof(mesh_access));
165
166 ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
167 if (ret)
168 return ret;
169
170 return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
171}
172
173/**
8973a6e7
RD
174 * lbs_anycast_set - Set function for sysfs attribute anycast_mask
175 * @dev: the &struct device
176 * @attr: device attributes
177 * @buf: buffer that contains new attribute value
178 * @count: size of buffer
e0e42da3
HS
179 */
180static ssize_t lbs_anycast_set(struct device *dev,
181 struct device_attribute *attr, const char * buf, size_t count)
182{
183 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
184 struct cmd_ds_mesh_access mesh_access;
185 uint32_t datum;
186 int ret;
187
188 memset(&mesh_access, 0, sizeof(mesh_access));
189 sscanf(buf, "%x", &datum);
190 mesh_access.data[0] = cpu_to_le32(datum);
191
192 ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
193 if (ret)
194 return ret;
195
196 return strlen(buf);
197}
198
199/**
8973a6e7
RD
200 * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit
201 * @dev: the &struct device
202 * @attr: device attributes
203 * @buf: buffer where data will be returned
e0e42da3
HS
204 */
205static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
206 struct device_attribute *attr, char *buf)
207{
208 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
209 struct cmd_ds_mesh_access mesh_access;
210 int ret;
211 u32 retry_limit;
212
213 memset(&mesh_access, 0, sizeof(mesh_access));
214 mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
215
216 ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
217 &mesh_access);
218 if (ret)
219 return ret;
220
221 retry_limit = le32_to_cpu(mesh_access.data[1]);
222 return snprintf(buf, 10, "%d\n", retry_limit);
223}
224
225/**
8973a6e7
RD
226 * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit
227 * @dev: the &struct device
228 * @attr: device attributes
229 * @buf: buffer that contains new attribute value
230 * @count: size of buffer
e0e42da3
HS
231 */
232static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
233 struct device_attribute *attr, const char *buf, size_t count)
234{
235 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
236 struct cmd_ds_mesh_access mesh_access;
237 int ret;
238 unsigned long retry_limit;
239
240 memset(&mesh_access, 0, sizeof(mesh_access));
241 mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
242
27d7f477 243 if (!kstrtoul(buf, 10, &retry_limit))
e0e42da3
HS
244 return -ENOTSUPP;
245 if (retry_limit > 15)
246 return -ENOTSUPP;
247
248 mesh_access.data[1] = cpu_to_le32(retry_limit);
249
250 ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
251 &mesh_access);
252 if (ret)
253 return ret;
254
255 return strlen(buf);
256}
257
258/**
8973a6e7
RD
259 * lbs_mesh_get - Get function for sysfs attribute mesh
260 * @dev: the &struct device
261 * @attr: device attributes
262 * @buf: buffer where data will be returned
e0e42da3
HS
263 */
264static ssize_t lbs_mesh_get(struct device *dev,
265 struct device_attribute *attr, char * buf)
266{
267 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
268 return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
269}
270
271/**
8973a6e7
RD
272 * lbs_mesh_set - Set function for sysfs attribute mesh
273 * @dev: the &struct device
274 * @attr: device attributes
275 * @buf: buffer that contains new attribute value
276 * @count: size of buffer
e0e42da3
HS
277 */
278static ssize_t lbs_mesh_set(struct device *dev,
279 struct device_attribute *attr, const char * buf, size_t count)
280{
281 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
282 int enable;
e0e42da3
HS
283
284 sscanf(buf, "%x", &enable);
285 enable = !!enable;
286 if (enable == !!priv->mesh_dev)
287 return count;
e0e42da3
HS
288
289 if (enable)
290 lbs_add_mesh(priv);
291 else
292 lbs_remove_mesh(priv);
293
294 return count;
295}
296
8973a6e7 297/*
e0e42da3
HS
298 * lbs_mesh attribute to be exported per ethX interface
299 * through sysfs (/sys/class/net/ethX/lbs_mesh)
300 */
301static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
302
8973a6e7 303/*
e0e42da3
HS
304 * anycast_mask attribute to be exported per mshX interface
305 * through sysfs (/sys/class/net/mshX/anycast_mask)
306 */
307static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
308
8973a6e7 309/*
e0e42da3
HS
310 * prb_rsp_limit attribute to be exported per mshX interface
311 * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
312 */
313static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
314 lbs_prb_rsp_limit_set);
315
316static struct attribute *lbs_mesh_sysfs_entries[] = {
317 &dev_attr_anycast_mask.attr,
318 &dev_attr_prb_rsp_limit.attr,
319 NULL,
320};
321
3db4f989 322static const struct attribute_group lbs_mesh_attr_group = {
e0e42da3
HS
323 .attrs = lbs_mesh_sysfs_entries,
324};
325
326
e0e42da3 327/***************************************************************************
3db4f989 328 * Persistent configuration support
e0e42da3
HS
329 */
330
3db4f989
DD
331static int mesh_get_default_parameters(struct device *dev,
332 struct mrvl_mesh_defaults *defs)
e0e42da3 333{
3db4f989
DD
334 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
335 struct cmd_ds_mesh_config cmd;
336 int ret;
e0e42da3 337
3db4f989
DD
338 memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
339 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
340 CMD_TYPE_MESH_GET_DEFAULTS);
e0e42da3 341
3db4f989
DD
342 if (ret)
343 return -EOPNOTSUPP;
c24ef46e 344
3db4f989 345 memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
c24ef46e 346
3db4f989
DD
347 return 0;
348}
e4da1a81 349
3db4f989
DD
350/**
351 * bootflag_get - Get function for sysfs attribute bootflag
352 * @dev: the &struct device
353 * @attr: device attributes
354 * @buf: buffer where data will be returned
355 */
356static ssize_t bootflag_get(struct device *dev,
357 struct device_attribute *attr, char *buf)
358{
359 struct mrvl_mesh_defaults defs;
360 int ret;
e0e42da3 361
3db4f989 362 ret = mesh_get_default_parameters(dev, &defs);
e0e42da3 363
3db4f989
DD
364 if (ret)
365 return ret;
e0e42da3 366
3db4f989 367 return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
e0e42da3
HS
368}
369
3db4f989
DD
370/**
371 * bootflag_set - Set function for sysfs attribute bootflag
372 * @dev: the &struct device
373 * @attr: device attributes
374 * @buf: buffer that contains new attribute value
375 * @count: size of buffer
376 */
377static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
378 const char *buf, size_t count)
e0e42da3 379{
3db4f989
DD
380 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
381 struct cmd_ds_mesh_config cmd;
382 uint32_t datum;
383 int ret;
e0e42da3 384
3db4f989
DD
385 memset(&cmd, 0, sizeof(cmd));
386 ret = sscanf(buf, "%d", &datum);
387 if ((ret != 1) || (datum > 1))
388 return -EINVAL;
e0e42da3 389
3db4f989
DD
390 *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
391 cmd.length = cpu_to_le16(sizeof(uint32_t));
392 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
393 CMD_TYPE_MESH_SET_BOOTFLAG);
394 if (ret)
395 return ret;
e0e42da3 396
3db4f989 397 return strlen(buf);
e0e42da3
HS
398}
399
e0e42da3 400/**
3db4f989
DD
401 * boottime_get - Get function for sysfs attribute boottime
402 * @dev: the &struct device
403 * @attr: device attributes
404 * @buf: buffer where data will be returned
e0e42da3 405 */
3db4f989
DD
406static ssize_t boottime_get(struct device *dev,
407 struct device_attribute *attr, char *buf)
e0e42da3 408{
3db4f989
DD
409 struct mrvl_mesh_defaults defs;
410 int ret;
e0e42da3 411
3db4f989 412 ret = mesh_get_default_parameters(dev, &defs);
e0e42da3 413
3db4f989
DD
414 if (ret)
415 return ret;
15dbaac0 416
fb904907 417 return snprintf(buf, 12, "%d\n", defs.boottime);
15dbaac0
JC
418}
419
420/**
8973a6e7
RD
421 * boottime_set - Set function for sysfs attribute boottime
422 * @dev: the &struct device
423 * @attr: device attributes
424 * @buf: buffer that contains new attribute value
425 * @count: size of buffer
15dbaac0
JC
426 */
427static ssize_t boottime_set(struct device *dev,
428 struct device_attribute *attr, const char *buf, size_t count)
429{
ab65f649 430 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
15dbaac0
JC
431 struct cmd_ds_mesh_config cmd;
432 uint32_t datum;
433 int ret;
434
435 memset(&cmd, 0, sizeof(cmd));
fb904907
BC
436 ret = sscanf(buf, "%d", &datum);
437 if ((ret != 1) || (datum > 255))
15dbaac0
JC
438 return -EINVAL;
439
440 /* A too small boot time will result in the device booting into
441 * standalone (no-host) mode before the host can take control of it,
442 * so the change will be hard to revert. This may be a desired
443 * feature (e.g to configure a very fast boot time for devices that
444 * will not be attached to a host), but dangerous. So I'm enforcing a
445 * lower limit of 20 seconds: remove and recompile the driver if this
446 * does not work for you.
447 */
448 datum = (datum < 20) ? 20 : datum;
449 cmd.data[0] = datum;
450 cmd.length = cpu_to_le16(sizeof(uint8_t));
451 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
452 CMD_TYPE_MESH_SET_BOOTTIME);
453 if (ret)
454 return ret;
455
456 return strlen(buf);
457}
458
b679aeb3 459/**
8973a6e7
RD
460 * channel_get - Get function for sysfs attribute channel
461 * @dev: the &struct device
462 * @attr: device attributes
463 * @buf: buffer where data will be returned
b679aeb3
JC
464 */
465static ssize_t channel_get(struct device *dev,
466 struct device_attribute *attr, char *buf)
467{
468 struct mrvl_mesh_defaults defs;
469 int ret;
470
471 ret = mesh_get_default_parameters(dev, &defs);
472
473 if (ret)
474 return ret;
475
fb904907 476 return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
b679aeb3
JC
477}
478
479/**
8973a6e7
RD
480 * channel_set - Set function for sysfs attribute channel
481 * @dev: the &struct device
482 * @attr: device attributes
483 * @buf: buffer that contains new attribute value
484 * @count: size of buffer
b679aeb3
JC
485 */
486static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
487 const char *buf, size_t count)
488{
ab65f649 489 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
b679aeb3 490 struct cmd_ds_mesh_config cmd;
fb904907 491 uint32_t datum;
b679aeb3
JC
492 int ret;
493
494 memset(&cmd, 0, sizeof(cmd));
fb904907 495 ret = sscanf(buf, "%d", &datum);
b679aeb3
JC
496 if (ret != 1 || datum < 1 || datum > 11)
497 return -EINVAL;
498
499 *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
500 cmd.length = cpu_to_le16(sizeof(uint16_t));
501 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
502 CMD_TYPE_MESH_SET_DEF_CHANNEL);
503 if (ret)
504 return ret;
505
506 return strlen(buf);
507}
508
15dbaac0 509/**
8973a6e7
RD
510 * mesh_id_get - Get function for sysfs attribute mesh_id
511 * @dev: the &struct device
512 * @attr: device attributes
513 * @buf: buffer where data will be returned
15dbaac0
JC
514 */
515static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
516 char *buf)
517{
518 struct mrvl_mesh_defaults defs;
15dbaac0
JC
519 int ret;
520
521 ret = mesh_get_default_parameters(dev, &defs);
522
523 if (ret)
524 return ret;
525
243e84e9 526 if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
f3a57fd1 527 dev_err(dev, "inconsistent mesh ID length\n");
243e84e9 528 defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
15dbaac0
JC
529 }
530
d89dba7a
DC
531 memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);
532 buf[defs.meshie.val.mesh_id_len] = '\n';
533 buf[defs.meshie.val.mesh_id_len + 1] = '\0';
15dbaac0 534
d89dba7a 535 return defs.meshie.val.mesh_id_len + 1;
15dbaac0
JC
536}
537
538/**
8973a6e7
RD
539 * mesh_id_set - Set function for sysfs attribute mesh_id
540 * @dev: the &struct device
541 * @attr: device attributes
542 * @buf: buffer that contains new attribute value
543 * @count: size of buffer
15dbaac0
JC
544 */
545static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
546 const char *buf, size_t count)
547{
548 struct cmd_ds_mesh_config cmd;
549 struct mrvl_mesh_defaults defs;
550 struct mrvl_meshie *ie;
ab65f649 551 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
15dbaac0
JC
552 int len;
553 int ret;
554
243e84e9 555 if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
15dbaac0
JC
556 return -EINVAL;
557
558 memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
559 ie = (struct mrvl_meshie *) &cmd.data[0];
560
561 /* fetch all other Information Element parameters */
562 ret = mesh_get_default_parameters(dev, &defs);
563
564 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
565
566 /* transfer IE elements */
567 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
568
569 len = count - 1;
570 memcpy(ie->val.mesh_id, buf, len);
571 /* SSID len */
572 ie->val.mesh_id_len = len;
573 /* IE len */
243e84e9 574 ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
15dbaac0
JC
575
576 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
577 CMD_TYPE_MESH_SET_MESH_IE);
578 if (ret)
579 return ret;
580
581 return strlen(buf);
582}
583
584/**
8973a6e7
RD
585 * protocol_id_get - Get function for sysfs attribute protocol_id
586 * @dev: the &struct device
587 * @attr: device attributes
588 * @buf: buffer where data will be returned
15dbaac0
JC
589 */
590static ssize_t protocol_id_get(struct device *dev,
591 struct device_attribute *attr, char *buf)
592{
593 struct mrvl_mesh_defaults defs;
594 int ret;
595
596 ret = mesh_get_default_parameters(dev, &defs);
597
598 if (ret)
599 return ret;
600
601 return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
602}
603
604/**
8973a6e7
RD
605 * protocol_id_set - Set function for sysfs attribute protocol_id
606 * @dev: the &struct device
607 * @attr: device attributes
608 * @buf: buffer that contains new attribute value
609 * @count: size of buffer
15dbaac0
JC
610 */
611static ssize_t protocol_id_set(struct device *dev,
612 struct device_attribute *attr, const char *buf, size_t count)
613{
614 struct cmd_ds_mesh_config cmd;
615 struct mrvl_mesh_defaults defs;
616 struct mrvl_meshie *ie;
ab65f649 617 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
15dbaac0
JC
618 uint32_t datum;
619 int ret;
620
621 memset(&cmd, 0, sizeof(cmd));
fb904907
BC
622 ret = sscanf(buf, "%d", &datum);
623 if ((ret != 1) || (datum > 255))
15dbaac0
JC
624 return -EINVAL;
625
626 /* fetch all other Information Element parameters */
627 ret = mesh_get_default_parameters(dev, &defs);
628
629 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
630
631 /* transfer IE elements */
632 ie = (struct mrvl_meshie *) &cmd.data[0];
633 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
634 /* update protocol id */
635 ie->val.active_protocol_id = datum;
636
637 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
638 CMD_TYPE_MESH_SET_MESH_IE);
639 if (ret)
640 return ret;
641
642 return strlen(buf);
643}
644
645/**
8973a6e7
RD
646 * metric_id_get - Get function for sysfs attribute metric_id
647 * @dev: the &struct device
648 * @attr: device attributes
649 * @buf: buffer where data will be returned
15dbaac0
JC
650 */
651static ssize_t metric_id_get(struct device *dev,
652 struct device_attribute *attr, char *buf)
653{
3db4f989
DD
654 struct mrvl_mesh_defaults defs;
655 int ret;
656
657 ret = mesh_get_default_parameters(dev, &defs);
658
659 if (ret)
660 return ret;
661
662 return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
663}
664
665/**
666 * metric_id_set - Set function for sysfs attribute metric_id
667 * @dev: the &struct device
668 * @attr: device attributes
669 * @buf: buffer that contains new attribute value
670 * @count: size of buffer
671 */
672static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
673 const char *buf, size_t count)
674{
675 struct cmd_ds_mesh_config cmd;
676 struct mrvl_mesh_defaults defs;
677 struct mrvl_meshie *ie;
678 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
679 uint32_t datum;
680 int ret;
681
682 memset(&cmd, 0, sizeof(cmd));
683 ret = sscanf(buf, "%d", &datum);
684 if ((ret != 1) || (datum > 255))
685 return -EINVAL;
686
687 /* fetch all other Information Element parameters */
688 ret = mesh_get_default_parameters(dev, &defs);
689
690 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
691
692 /* transfer IE elements */
693 ie = (struct mrvl_meshie *) &cmd.data[0];
694 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
695 /* update metric id */
696 ie->val.active_metric_id = datum;
697
698 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
699 CMD_TYPE_MESH_SET_MESH_IE);
700 if (ret)
701 return ret;
702
703 return strlen(buf);
704}
705
706/**
707 * capability_get - Get function for sysfs attribute capability
708 * @dev: the &struct device
709 * @attr: device attributes
710 * @buf: buffer where data will be returned
711 */
712static ssize_t capability_get(struct device *dev,
713 struct device_attribute *attr, char *buf)
714{
715 struct mrvl_mesh_defaults defs;
716 int ret;
717
718 ret = mesh_get_default_parameters(dev, &defs);
719
720 if (ret)
721 return ret;
722
723 return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
724}
725
726/**
727 * capability_set - Set function for sysfs attribute capability
728 * @dev: the &struct device
729 * @attr: device attributes
730 * @buf: buffer that contains new attribute value
731 * @count: size of buffer
732 */
733static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
734 const char *buf, size_t count)
735{
736 struct cmd_ds_mesh_config cmd;
737 struct mrvl_mesh_defaults defs;
738 struct mrvl_meshie *ie;
739 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
740 uint32_t datum;
741 int ret;
742
743 memset(&cmd, 0, sizeof(cmd));
744 ret = sscanf(buf, "%d", &datum);
745 if ((ret != 1) || (datum > 255))
746 return -EINVAL;
747
748 /* fetch all other Information Element parameters */
749 ret = mesh_get_default_parameters(dev, &defs);
750
751 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
752
753 /* transfer IE elements */
754 ie = (struct mrvl_meshie *) &cmd.data[0];
755 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
756 /* update value */
757 ie->val.mesh_capability = datum;
758
759 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
760 CMD_TYPE_MESH_SET_MESH_IE);
761 if (ret)
762 return ret;
763
764 return strlen(buf);
765}
766
767
768static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
769static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
770static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
771static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
772static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
773static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
774static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
775
776static struct attribute *boot_opts_attrs[] = {
777 &dev_attr_bootflag.attr,
778 &dev_attr_boottime.attr,
779 &dev_attr_channel.attr,
780 NULL
781};
782
783static const struct attribute_group boot_opts_group = {
784 .name = "boot_options",
785 .attrs = boot_opts_attrs,
786};
787
788static struct attribute *mesh_ie_attrs[] = {
789 &dev_attr_mesh_id.attr,
790 &dev_attr_protocol_id.attr,
791 &dev_attr_metric_id.attr,
792 &dev_attr_capability.attr,
793 NULL
794};
795
796static const struct attribute_group mesh_ie_group = {
797 .name = "mesh_ie",
798 .attrs = mesh_ie_attrs,
799};
800
801static void lbs_persist_config_init(struct net_device *dev)
802{
803 int ret;
804 ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
805 ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
806}
807
808static void lbs_persist_config_remove(struct net_device *dev)
809{
810 sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
811 sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
812}
813
814
815/***************************************************************************
816 * Initializing and starting, stopping mesh
817 */
818
819/*
820 * Check mesh FW version and appropriately send the mesh start
821 * command
822 */
823int lbs_init_mesh(struct lbs_private *priv)
824{
3db4f989
DD
825 int ret = 0;
826
827 lbs_deb_enter(LBS_DEB_MESH);
828
3db4f989
DD
829 /* Determine mesh_fw_ver from fwrelease and fwcapinfo */
830 /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
831 /* 5.110.22 have mesh command with 0xa3 command id */
832 /* 10.0.0.p0 FW brings in mesh config command with different id */
833 /* Check FW version MSB and initialize mesh_fw_ver */
834 if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
835 /* Enable mesh, if supported, and work out which TLV it uses.
836 0x100 + 291 is an unofficial value used in 5.110.20.pXX
837 0x100 + 37 is the official value used in 5.110.21.pXX
838 but we check them in that order because 20.pXX doesn't
839 give an error -- it just silently fails. */
840
841 /* 5.110.20.pXX firmware will fail the command if the channel
842 doesn't match the existing channel. But only if the TLV
843 is correct. If the channel is wrong, _BOTH_ versions will
844 give an error to 0x100+291, and allow 0x100+37 to succeed.
845 It's just that 5.110.20.pXX will not have done anything
846 useful */
847
848 priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
49fee692 849 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
3db4f989 850 priv->mesh_tlv = TLV_TYPE_MESH_ID;
49fee692 851 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
3db4f989
DD
852 priv->mesh_tlv = 0;
853 }
854 } else
855 if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
856 (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
857 /* 10.0.0.pXX new firmwares should succeed with TLV
858 * 0x100+37; Do not invoke command with old TLV.
859 */
860 priv->mesh_tlv = TLV_TYPE_MESH_ID;
49fee692 861 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
3db4f989
DD
862 priv->mesh_tlv = 0;
863 }
864
d9319986 865 /* Stop meshing until interface is brought up */
49fee692 866 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
3db4f989
DD
867
868 if (priv->mesh_tlv) {
869 sprintf(priv->mesh_ssid, "mesh");
870 priv->mesh_ssid_len = 4;
3db4f989
DD
871 ret = 1;
872 }
873
874 lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
875 return ret;
876}
877
49fee692
DD
878void lbs_start_mesh(struct lbs_private *priv)
879{
880 lbs_add_mesh(priv);
881
882 if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
883 netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
884}
3db4f989
DD
885
886int lbs_deinit_mesh(struct lbs_private *priv)
887{
888 struct net_device *dev = priv->dev;
889 int ret = 0;
15dbaac0 890
3db4f989 891 lbs_deb_enter(LBS_DEB_MESH);
15dbaac0 892
3db4f989
DD
893 if (priv->mesh_tlv) {
894 device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
895 ret = 1;
896 }
15dbaac0 897
3db4f989
DD
898 lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
899 return ret;
15dbaac0
JC
900}
901
3db4f989 902
15dbaac0 903/**
3db4f989
DD
904 * lbs_mesh_stop - close the mshX interface
905 *
906 * @dev: A pointer to &net_device structure
907 * returns: 0
15dbaac0 908 */
3db4f989 909static int lbs_mesh_stop(struct net_device *dev)
15dbaac0 910{
3db4f989 911 struct lbs_private *priv = dev->ml_priv;
15dbaac0 912
3db4f989 913 lbs_deb_enter(LBS_DEB_MESH);
49fee692
DD
914 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
915 lbs_mesh_get_channel(priv));
15dbaac0 916
d9319986 917 spin_lock_irq(&priv->driver_lock);
15dbaac0 918
3db4f989
DD
919 netif_stop_queue(dev);
920 netif_carrier_off(dev);
15dbaac0 921
3db4f989 922 spin_unlock_irq(&priv->driver_lock);
15dbaac0 923
d2e7b342
DD
924 lbs_update_mcast(priv);
925 if (!lbs_iface_active(priv))
926 lbs_stop_iface(priv);
15dbaac0 927
3db4f989
DD
928 lbs_deb_leave(LBS_DEB_MESH);
929 return 0;
15dbaac0
JC
930}
931
932/**
3db4f989
DD
933 * lbs_mesh_dev_open - open the mshX interface
934 *
935 * @dev: A pointer to &net_device structure
936 * returns: 0 or -EBUSY if monitor mode active
15dbaac0 937 */
3db4f989 938static int lbs_mesh_dev_open(struct net_device *dev)
15dbaac0 939{
3db4f989
DD
940 struct lbs_private *priv = dev->ml_priv;
941 int ret = 0;
15dbaac0 942
3db4f989 943 lbs_deb_enter(LBS_DEB_NET);
d2e7b342
DD
944 if (!priv->iface_running) {
945 ret = lbs_start_iface(priv);
946 if (ret)
947 goto out;
948 }
15dbaac0 949
3db4f989 950 spin_lock_irq(&priv->driver_lock);
15dbaac0 951
3db4f989
DD
952 if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
953 ret = -EBUSY;
d9319986 954 spin_unlock_irq(&priv->driver_lock);
3db4f989
DD
955 goto out;
956 }
957
3db4f989
DD
958 netif_carrier_on(dev);
959
960 if (!priv->tx_pending_len)
961 netif_wake_queue(dev);
3db4f989
DD
962
963 spin_unlock_irq(&priv->driver_lock);
d9319986 964
49fee692
DD
965 ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
966 lbs_mesh_get_channel(priv));
d9319986
DD
967
968out:
3db4f989
DD
969 lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
970 return ret;
15dbaac0
JC
971}
972
3db4f989
DD
973static const struct net_device_ops mesh_netdev_ops = {
974 .ndo_open = lbs_mesh_dev_open,
975 .ndo_stop = lbs_mesh_stop,
976 .ndo_start_xmit = lbs_hard_start_xmit,
977 .ndo_set_mac_address = lbs_set_mac_address,
afc4b13d 978 .ndo_set_rx_mode = lbs_set_multicast_list,
3db4f989
DD
979};
980
15dbaac0 981/**
3db4f989
DD
982 * lbs_add_mesh - add mshX interface
983 *
984 * @priv: A pointer to the &struct lbs_private structure
985 * returns: 0 if successful, -X otherwise
15dbaac0 986 */
3db4f989 987static int lbs_add_mesh(struct lbs_private *priv)
15dbaac0 988{
3db4f989 989 struct net_device *mesh_dev = NULL;
49fee692 990 struct wireless_dev *mesh_wdev;
3db4f989 991 int ret = 0;
15dbaac0 992
3db4f989 993 lbs_deb_enter(LBS_DEB_MESH);
15dbaac0 994
3db4f989 995 /* Allocate a virtual mesh device */
49fee692
DD
996 mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
997 if (!mesh_wdev) {
998 lbs_deb_mesh("init mshX wireless device failed\n");
999 ret = -ENOMEM;
1000 goto done;
1001 }
1002
3db4f989
DD
1003 mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
1004 if (!mesh_dev) {
1005 lbs_deb_mesh("init mshX device failed\n");
1006 ret = -ENOMEM;
49fee692 1007 goto err_free_wdev;
3db4f989 1008 }
49fee692
DD
1009
1010 mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
1011 mesh_wdev->wiphy = priv->wdev->wiphy;
1012 mesh_wdev->netdev = mesh_dev;
1013
3db4f989 1014 mesh_dev->ml_priv = priv;
49fee692 1015 mesh_dev->ieee80211_ptr = mesh_wdev;
3db4f989 1016 priv->mesh_dev = mesh_dev;
15dbaac0 1017
3db4f989
DD
1018 mesh_dev->netdev_ops = &mesh_netdev_ops;
1019 mesh_dev->ethtool_ops = &lbs_ethtool_ops;
1020 memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN);
15dbaac0 1021
3db4f989 1022 SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
15dbaac0 1023
3db4f989
DD
1024 mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
1025 /* Register virtual mesh interface */
1026 ret = register_netdev(mesh_dev);
1027 if (ret) {
1028 pr_err("cannot register mshX virtual interface\n");
49fee692 1029 goto err_free_netdev;
3db4f989
DD
1030 }
1031
1032 ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
15dbaac0 1033 if (ret)
3db4f989 1034 goto err_unregister;
15dbaac0 1035
3db4f989 1036 lbs_persist_config_init(mesh_dev);
15dbaac0 1037
3db4f989
DD
1038 /* Everything successful */
1039 ret = 0;
1040 goto done;
15dbaac0 1041
3db4f989
DD
1042err_unregister:
1043 unregister_netdev(mesh_dev);
15dbaac0 1044
49fee692 1045err_free_netdev:
3db4f989 1046 free_netdev(mesh_dev);
15dbaac0 1047
49fee692
DD
1048err_free_wdev:
1049 kfree(mesh_wdev);
1050
3db4f989
DD
1051done:
1052 lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
1053 return ret;
1054}
15dbaac0 1055
3db4f989
DD
1056void lbs_remove_mesh(struct lbs_private *priv)
1057{
1058 struct net_device *mesh_dev;
15dbaac0 1059
3db4f989
DD
1060 mesh_dev = priv->mesh_dev;
1061 if (!mesh_dev)
1062 return;
15dbaac0 1063
3db4f989
DD
1064 lbs_deb_enter(LBS_DEB_MESH);
1065 netif_stop_queue(mesh_dev);
1066 netif_carrier_off(mesh_dev);
1067 sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
1068 lbs_persist_config_remove(mesh_dev);
1069 unregister_netdev(mesh_dev);
1070 priv->mesh_dev = NULL;
49fee692 1071 kfree(mesh_dev->ieee80211_ptr);
3db4f989
DD
1072 free_netdev(mesh_dev);
1073 lbs_deb_leave(LBS_DEB_MESH);
15dbaac0
JC
1074}
1075
3db4f989
DD
1076
1077/***************************************************************************
1078 * Sending and receiving
1079 */
1080struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
1081 struct net_device *dev, struct rxpd *rxpd)
15dbaac0 1082{
3db4f989
DD
1083 if (priv->mesh_dev) {
1084 if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
1085 if (rxpd->rx_control & RxPD_MESH_FRAME)
1086 dev = priv->mesh_dev;
1087 } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
1088 if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
1089 dev = priv->mesh_dev;
1090 }
1091 }
1092 return dev;
15dbaac0 1093}
c7fe64cf
HS
1094
1095
3db4f989
DD
1096void lbs_mesh_set_txpd(struct lbs_private *priv,
1097 struct net_device *dev, struct txpd *txpd)
1098{
1099 if (dev == priv->mesh_dev) {
1100 if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
1101 txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
1102 else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
1103 txpd->u.bss.bss_num = MESH_IFACE_ID;
1104 }
1105}
1106
c7fe64cf
HS
1107
1108/***************************************************************************
1109 * Ethtool related
1110 */
1111
3db4f989 1112static const char * const mesh_stat_strings[] = {
c7fe64cf
HS
1113 "drop_duplicate_bcast",
1114 "drop_ttl_zero",
1115 "drop_no_fwd_route",
1116 "drop_no_buffers",
1117 "fwded_unicast_cnt",
1118 "fwded_bcast_cnt",
1119 "drop_blind_table",
1120 "tx_failed_cnt"
1121};
1122
1123void lbs_mesh_ethtool_get_stats(struct net_device *dev,
1124 struct ethtool_stats *stats, uint64_t *data)
1125{
1126 struct lbs_private *priv = dev->ml_priv;
1127 struct cmd_ds_mesh_access mesh_access;
1128 int ret;
1129
1130 lbs_deb_enter(LBS_DEB_ETHTOOL);
1131
1132 /* Get Mesh Statistics */
1133 ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
1134
1135 if (ret) {
1136 memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
1137 return;
1138 }
1139
1140 priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
1141 priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
1142 priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
1143 priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
1144 priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
1145 priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
1146 priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
1147 priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
1148
1149 data[0] = priv->mstats.fwd_drop_rbt;
1150 data[1] = priv->mstats.fwd_drop_ttl;
1151 data[2] = priv->mstats.fwd_drop_noroute;
1152 data[3] = priv->mstats.fwd_drop_nobuf;
1153 data[4] = priv->mstats.fwd_unicast_cnt;
1154 data[5] = priv->mstats.fwd_bcast_cnt;
1155 data[6] = priv->mstats.drop_blind;
1156 data[7] = priv->mstats.tx_failed_cnt;
1157
1158 lbs_deb_enter(LBS_DEB_ETHTOOL);
1159}
1160
1161int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)
1162{
1163 struct lbs_private *priv = dev->ml_priv;
1164
1165 if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
1166 return MESH_STATS_NUM;
1167
1168 return -EOPNOTSUPP;
1169}
1170
1171void lbs_mesh_ethtool_get_strings(struct net_device *dev,
1172 uint32_t stringset, uint8_t *s)
1173{
1174 int i;
1175
1176 lbs_deb_enter(LBS_DEB_ETHTOOL);
1177
1178 switch (stringset) {
1179 case ETH_SS_STATS:
1180 for (i = 0; i < MESH_STATS_NUM; i++) {
1181 memcpy(s + i * ETH_GSTRING_LEN,
1182 mesh_stat_strings[i],
1183 ETH_GSTRING_LEN);
1184 }
1185 break;
1186 }
1187 lbs_deb_enter(LBS_DEB_ETHTOOL);
1188}
This page took 0.97679 seconds and 5 git commands to generate.