blob: 3e81264db81e937d25b3dda0a5fb7543d4c4379e [file] [log] [blame]
Joe Perches0e4e06a2011-05-02 16:49:14 -07001#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2
Javier Cardona15dbaac2008-05-17 21:01:24 -07003#include <linux/delay.h>
4#include <linux/etherdevice.h>
Alexey Dobriyana6b7a402011-06-06 10:43:46 +00005#include <linux/hardirq.h>
Javier Cardona15dbaac2008-05-17 21:01:24 -07006#include <linux/netdevice.h>
Daniel Drake1f044932009-12-24 08:11:24 +00007#include <linux/if_ether.h>
Javier Cardona15dbaac2008-05-17 21:01:24 -07008#include <linux/if_arp.h>
9#include <linux/kthread.h>
10#include <linux/kfifo.h>
Kiran Divekare86dc1c2010-06-14 22:01:26 +053011#include <net/cfg80211.h>
Javier Cardona15dbaac2008-05-17 21:01:24 -070012
Holger Schurige0e42da2009-11-25 13:10:15 +010013#include "mesh.h"
Javier Cardona15dbaac2008-05-17 21:01:24 -070014#include "decl.h"
Javier Cardona15dbaac2008-05-17 21:01:24 -070015#include "cmd.h"
16
Holger Schurige0e42da2009-11-25 13:10:15 +010017
Daniel Drake3db4f982011-07-20 17:53:50 +010018static 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:
Arend van Spriel04b23122012-10-12 12:28:14 +0200104 ie->id = WLAN_EID_VENDOR_SPECIFIC;
Daniel Drake3db4f982011-07-20 17:53:50 +0100105 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
Daniel Drake49fee692011-07-21 20:43:17 +0100132int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
133{
Johannes Berge8c9bd52012-06-06 08:18:22 +0200134 priv->mesh_channel = channel;
Daniel Drake49fee692011-07-21 20:43:17 +0100135 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{
Johannes Berge8c9bd52012-06-06 08:18:22 +0200140 return priv->mesh_channel ?: 1;
Daniel Drake49fee692011-07-21 20:43:17 +0100141}
Daniel Drake3db4f982011-07-20 17:53:50 +0100142
Holger Schurige0e42da2009-11-25 13:10:15 +0100143/***************************************************************************
144 * Mesh sysfs support
145 */
146
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700147/*
Holger Schurige0e42da2009-11-25 13:10:15 +0100148 * Attributes exported through sysfs
149 */
150
151/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700152 * 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
Holger Schurige0e42da2009-11-25 13:10:15 +0100156 */
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700174 * 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
Holger Schurige0e42da2009-11-25 13:10:15 +0100179 */
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700200 * 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
Holger Schurige0e42da2009-11-25 13:10:15 +0100204 */
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700226 * 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
Holger Schurige0e42da2009-11-25 13:10:15 +0100231 */
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
243 if (!strict_strtoul(buf, 10, &retry_limit))
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700259 * 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
Holger Schurige0e42da2009-11-25 13:10:15 +0100263 */
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700272 * 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
Holger Schurige0e42da2009-11-25 13:10:15 +0100277 */
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;
Holger Schurige0e42da2009-11-25 13:10:15 +0100283
284 sscanf(buf, "%x", &enable);
285 enable = !!enable;
286 if (enable == !!priv->mesh_dev)
287 return count;
Holger Schurige0e42da2009-11-25 13:10:15 +0100288
289 if (enable)
290 lbs_add_mesh(priv);
291 else
292 lbs_remove_mesh(priv);
293
294 return count;
295}
296
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700297/*
Holger Schurige0e42da2009-11-25 13:10:15 +0100298 * 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
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700303/*
Holger Schurige0e42da2009-11-25 13:10:15 +0100304 * 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
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700309/*
Holger Schurige0e42da2009-11-25 13:10:15 +0100310 * 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
Daniel Drake3db4f982011-07-20 17:53:50 +0100322static const struct attribute_group lbs_mesh_attr_group = {
Holger Schurige0e42da2009-11-25 13:10:15 +0100323 .attrs = lbs_mesh_sysfs_entries,
324};
325
326
Holger Schurigece1e3c2009-11-25 13:11:16 +0100327/***************************************************************************
Holger Schurige0e42da2009-11-25 13:10:15 +0100328 * Persistent configuration support
329 */
330
Javier Cardona15dbaac2008-05-17 21:01:24 -0700331static int mesh_get_default_parameters(struct device *dev,
332 struct mrvl_mesh_defaults *defs)
333{
Kiran Divekarab65f642009-02-19 19:32:39 -0500334 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700335 struct cmd_ds_mesh_config cmd;
336 int ret;
337
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);
341
342 if (ret)
343 return -EOPNOTSUPP;
344
345 memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
346
347 return 0;
348}
349
350/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700351 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700355 */
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;
361
362 ret = mesh_get_default_parameters(dev, &defs);
363
364 if (ret)
365 return ret;
366
Brian Cavagnolofb904902008-07-16 12:15:26 -0700367 return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
Javier Cardona15dbaac2008-05-17 21:01:24 -0700368}
369
370/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700371 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700376 */
377static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
378 const char *buf, size_t count)
379{
Kiran Divekarab65f642009-02-19 19:32:39 -0500380 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700381 struct cmd_ds_mesh_config cmd;
382 uint32_t datum;
383 int ret;
384
385 memset(&cmd, 0, sizeof(cmd));
Brian Cavagnolofb904902008-07-16 12:15:26 -0700386 ret = sscanf(buf, "%d", &datum);
387 if ((ret != 1) || (datum > 1))
Javier Cardona15dbaac2008-05-17 21:01:24 -0700388 return -EINVAL;
389
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;
396
397 return strlen(buf);
398}
399
400/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700401 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700405 */
406static ssize_t boottime_get(struct device *dev,
407 struct device_attribute *attr, char *buf)
408{
409 struct mrvl_mesh_defaults defs;
410 int ret;
411
412 ret = mesh_get_default_parameters(dev, &defs);
413
414 if (ret)
415 return ret;
416
Brian Cavagnolofb904902008-07-16 12:15:26 -0700417 return snprintf(buf, 12, "%d\n", defs.boottime);
Javier Cardona15dbaac2008-05-17 21:01:24 -0700418}
419
420/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700421 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700426 */
427static ssize_t boottime_set(struct device *dev,
428 struct device_attribute *attr, const char *buf, size_t count)
429{
Kiran Divekarab65f642009-02-19 19:32:39 -0500430 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700431 struct cmd_ds_mesh_config cmd;
432 uint32_t datum;
433 int ret;
434
435 memset(&cmd, 0, sizeof(cmd));
Brian Cavagnolofb904902008-07-16 12:15:26 -0700436 ret = sscanf(buf, "%d", &datum);
437 if ((ret != 1) || (datum > 255))
Javier Cardona15dbaac2008-05-17 21:01:24 -0700438 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
459/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700460 * 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
Javier Cardonab679aeb2008-05-20 15:18:49 -0700464 */
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
Brian Cavagnolofb904902008-07-16 12:15:26 -0700476 return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
Javier Cardonab679aeb2008-05-20 15:18:49 -0700477}
478
479/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700480 * 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
Javier Cardonab679aeb2008-05-20 15:18:49 -0700485 */
486static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
487 const char *buf, size_t count)
488{
Kiran Divekarab65f642009-02-19 19:32:39 -0500489 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardonab679aeb2008-05-20 15:18:49 -0700490 struct cmd_ds_mesh_config cmd;
Brian Cavagnolofb904902008-07-16 12:15:26 -0700491 uint32_t datum;
Javier Cardonab679aeb2008-05-20 15:18:49 -0700492 int ret;
493
494 memset(&cmd, 0, sizeof(cmd));
Brian Cavagnolofb904902008-07-16 12:15:26 -0700495 ret = sscanf(buf, "%d", &datum);
Javier Cardonab679aeb2008-05-20 15:18:49 -0700496 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
509/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700510 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700514 */
515static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
516 char *buf)
517{
518 struct mrvl_mesh_defaults defs;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700519 int ret;
520
521 ret = mesh_get_default_parameters(dev, &defs);
522
523 if (ret)
524 return ret;
525
Holger Schurig243e84e2009-10-22 15:30:47 +0200526 if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
Joe Perchesf3a57fd2011-05-02 16:49:15 -0700527 dev_err(dev, "inconsistent mesh ID length\n");
Holger Schurig243e84e2009-10-22 15:30:47 +0200528 defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700529 }
530
Dan Carpenterd89dba72011-03-10 18:23:26 +0300531 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';
Javier Cardona15dbaac2008-05-17 21:01:24 -0700534
Dan Carpenterd89dba72011-03-10 18:23:26 +0300535 return defs.meshie.val.mesh_id_len + 1;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700536}
537
538/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700539 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700544 */
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;
Kiran Divekarab65f642009-02-19 19:32:39 -0500551 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700552 int len;
553 int ret;
554
Holger Schurig243e84e2009-10-22 15:30:47 +0200555 if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
Javier Cardona15dbaac2008-05-17 21:01:24 -0700556 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 */
Holger Schurig243e84e2009-10-22 15:30:47 +0200574 ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700575
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700585 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700589 */
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700605 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700610 */
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;
Kiran Divekarab65f642009-02-19 19:32:39 -0500617 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700618 uint32_t datum;
619 int ret;
620
621 memset(&cmd, 0, sizeof(cmd));
Brian Cavagnolofb904902008-07-16 12:15:26 -0700622 ret = sscanf(buf, "%d", &datum);
623 if ((ret != 1) || (datum > 255))
Javier Cardona15dbaac2008-05-17 21:01:24 -0700624 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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700646 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700650 */
651static ssize_t metric_id_get(struct device *dev,
652 struct device_attribute *attr, char *buf)
653{
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700666 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700671 */
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;
Kiran Divekarab65f642009-02-19 19:32:39 -0500678 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700679 uint32_t datum;
680 int ret;
681
682 memset(&cmd, 0, sizeof(cmd));
Brian Cavagnolofb904902008-07-16 12:15:26 -0700683 ret = sscanf(buf, "%d", &datum);
684 if ((ret != 1) || (datum > 255))
Javier Cardona15dbaac2008-05-17 21:01:24 -0700685 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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700707 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700711 */
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/**
Randy Dunlap8973a6e2011-04-26 15:25:29 -0700727 * 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
Javier Cardona15dbaac2008-05-17 21:01:24 -0700732 */
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;
Kiran Divekarab65f642009-02-19 19:32:39 -0500739 struct lbs_private *priv = to_net_dev(dev)->ml_priv;
Javier Cardona15dbaac2008-05-17 21:01:24 -0700740 uint32_t datum;
741 int ret;
742
743 memset(&cmd, 0, sizeof(cmd));
Brian Cavagnolofb904902008-07-16 12:15:26 -0700744 ret = sscanf(buf, "%d", &datum);
745 if ((ret != 1) || (datum > 255))
Javier Cardona15dbaac2008-05-17 21:01:24 -0700746 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);
Javier Cardonab679aeb2008-05-20 15:18:49 -0700770static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
Javier Cardona15dbaac2008-05-17 21:01:24 -0700771static 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,
Javier Cardonab679aeb2008-05-20 15:18:49 -0700779 &dev_attr_channel.attr,
Javier Cardona15dbaac2008-05-17 21:01:24 -0700780 NULL
781};
782
Daniel Drake3db4f982011-07-20 17:53:50 +0100783static const struct attribute_group boot_opts_group = {
Javier Cardona15dbaac2008-05-17 21:01:24 -0700784 .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
Daniel Drake3db4f982011-07-20 17:53:50 +0100796static const struct attribute_group mesh_ie_group = {
Javier Cardona15dbaac2008-05-17 21:01:24 -0700797 .name = "mesh_ie",
798 .attrs = mesh_ie_attrs,
799};
800
Daniel Drake3db4f982011-07-20 17:53:50 +0100801static void lbs_persist_config_init(struct net_device *dev)
Javier Cardona15dbaac2008-05-17 21:01:24 -0700802{
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
Daniel Drake3db4f982011-07-20 17:53:50 +0100808static void lbs_persist_config_remove(struct net_device *dev)
Javier Cardona15dbaac2008-05-17 21:01:24 -0700809{
810 sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
811 sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
812}
Holger Schurigc7fe64c2009-11-25 13:10:49 +0100813
814
Daniel Drake3db4f982011-07-20 17:53:50 +0100815/***************************************************************************
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{
Daniel Drake3db4f982011-07-20 17:53:50 +0100825 int ret = 0;
826
827 lbs_deb_enter(LBS_DEB_MESH);
828
Daniel Drake3db4f982011-07-20 17:53:50 +0100829 /* 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;
Daniel Drake49fee692011-07-21 20:43:17 +0100849 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
Daniel Drake3db4f982011-07-20 17:53:50 +0100850 priv->mesh_tlv = TLV_TYPE_MESH_ID;
Daniel Drake49fee692011-07-21 20:43:17 +0100851 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
Daniel Drake3db4f982011-07-20 17:53:50 +0100852 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;
Daniel Drake49fee692011-07-21 20:43:17 +0100861 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
Daniel Drake3db4f982011-07-20 17:53:50 +0100862 priv->mesh_tlv = 0;
863 }
864
Daniel Draked9319982011-07-20 17:53:56 +0100865 /* Stop meshing until interface is brought up */
Daniel Drake49fee692011-07-21 20:43:17 +0100866 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
Daniel Drake3db4f982011-07-20 17:53:50 +0100867
868 if (priv->mesh_tlv) {
869 sprintf(priv->mesh_ssid, "mesh");
870 priv->mesh_ssid_len = 4;
Daniel Drake3db4f982011-07-20 17:53:50 +0100871 ret = 1;
872 }
873
874 lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
875 return ret;
876}
877
Daniel Drake49fee692011-07-21 20:43:17 +0100878void 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}
Daniel Drake3db4f982011-07-20 17:53:50 +0100885
886int lbs_deinit_mesh(struct lbs_private *priv)
887{
888 struct net_device *dev = priv->dev;
889 int ret = 0;
890
891 lbs_deb_enter(LBS_DEB_MESH);
892
893 if (priv->mesh_tlv) {
894 device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
895 ret = 1;
896 }
897
898 lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
899 return ret;
900}
901
902
903/**
904 * lbs_mesh_stop - close the mshX interface
905 *
906 * @dev: A pointer to &net_device structure
907 * returns: 0
908 */
909static int lbs_mesh_stop(struct net_device *dev)
910{
911 struct lbs_private *priv = dev->ml_priv;
912
913 lbs_deb_enter(LBS_DEB_MESH);
Daniel Drake49fee692011-07-21 20:43:17 +0100914 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
915 lbs_mesh_get_channel(priv));
Daniel Drake3db4f982011-07-20 17:53:50 +0100916
Daniel Draked9319982011-07-20 17:53:56 +0100917 spin_lock_irq(&priv->driver_lock);
Daniel Drake3db4f982011-07-20 17:53:50 +0100918
919 netif_stop_queue(dev);
920 netif_carrier_off(dev);
921
922 spin_unlock_irq(&priv->driver_lock);
923
Daniel Draked2e7b342011-08-01 16:43:13 +0100924 lbs_update_mcast(priv);
925 if (!lbs_iface_active(priv))
926 lbs_stop_iface(priv);
Daniel Drake3db4f982011-07-20 17:53:50 +0100927
928 lbs_deb_leave(LBS_DEB_MESH);
929 return 0;
930}
931
932/**
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
937 */
938static int lbs_mesh_dev_open(struct net_device *dev)
939{
940 struct lbs_private *priv = dev->ml_priv;
941 int ret = 0;
942
943 lbs_deb_enter(LBS_DEB_NET);
Daniel Draked2e7b342011-08-01 16:43:13 +0100944 if (!priv->iface_running) {
945 ret = lbs_start_iface(priv);
946 if (ret)
947 goto out;
948 }
Daniel Drake3db4f982011-07-20 17:53:50 +0100949
950 spin_lock_irq(&priv->driver_lock);
951
952 if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
953 ret = -EBUSY;
Daniel Draked9319982011-07-20 17:53:56 +0100954 spin_unlock_irq(&priv->driver_lock);
Daniel Drake3db4f982011-07-20 17:53:50 +0100955 goto out;
956 }
957
Daniel Drake3db4f982011-07-20 17:53:50 +0100958 netif_carrier_on(dev);
959
960 if (!priv->tx_pending_len)
961 netif_wake_queue(dev);
Daniel Drake3db4f982011-07-20 17:53:50 +0100962
963 spin_unlock_irq(&priv->driver_lock);
Daniel Draked9319982011-07-20 17:53:56 +0100964
Daniel Drake49fee692011-07-21 20:43:17 +0100965 ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
966 lbs_mesh_get_channel(priv));
Daniel Draked9319982011-07-20 17:53:56 +0100967
968out:
Daniel Drake3db4f982011-07-20 17:53:50 +0100969 lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
970 return ret;
971}
972
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,
Jiri Pirkoafc4b132011-08-16 06:29:01 +0000978 .ndo_set_rx_mode = lbs_set_multicast_list,
Daniel Drake3db4f982011-07-20 17:53:50 +0100979};
980
981/**
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
986 */
987static int lbs_add_mesh(struct lbs_private *priv)
988{
989 struct net_device *mesh_dev = NULL;
Daniel Drake49fee692011-07-21 20:43:17 +0100990 struct wireless_dev *mesh_wdev;
Daniel Drake3db4f982011-07-20 17:53:50 +0100991 int ret = 0;
992
993 lbs_deb_enter(LBS_DEB_MESH);
994
995 /* Allocate a virtual mesh device */
Daniel Drake49fee692011-07-21 20:43:17 +0100996 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
Daniel Drake3db4f982011-07-20 17:53:50 +01001003 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;
Daniel Drake49fee692011-07-21 20:43:17 +01001007 goto err_free_wdev;
Daniel Drake3db4f982011-07-20 17:53:50 +01001008 }
Daniel Drake49fee692011-07-21 20:43:17 +01001009
1010 mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
1011 mesh_wdev->wiphy = priv->wdev->wiphy;
1012 mesh_wdev->netdev = mesh_dev;
1013
Daniel Drake3db4f982011-07-20 17:53:50 +01001014 mesh_dev->ml_priv = priv;
Daniel Drake49fee692011-07-21 20:43:17 +01001015 mesh_dev->ieee80211_ptr = mesh_wdev;
Daniel Drake3db4f982011-07-20 17:53:50 +01001016 priv->mesh_dev = mesh_dev;
1017
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);
1021
1022 SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
1023
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");
Daniel Drake49fee692011-07-21 20:43:17 +01001029 goto err_free_netdev;
Daniel Drake3db4f982011-07-20 17:53:50 +01001030 }
1031
1032 ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
1033 if (ret)
1034 goto err_unregister;
1035
1036 lbs_persist_config_init(mesh_dev);
1037
1038 /* Everything successful */
1039 ret = 0;
1040 goto done;
1041
1042err_unregister:
1043 unregister_netdev(mesh_dev);
1044
Daniel Drake49fee692011-07-21 20:43:17 +01001045err_free_netdev:
Daniel Drake3db4f982011-07-20 17:53:50 +01001046 free_netdev(mesh_dev);
1047
Daniel Drake49fee692011-07-21 20:43:17 +01001048err_free_wdev:
1049 kfree(mesh_wdev);
1050
Daniel Drake3db4f982011-07-20 17:53:50 +01001051done:
1052 lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
1053 return ret;
1054}
1055
1056void lbs_remove_mesh(struct lbs_private *priv)
1057{
1058 struct net_device *mesh_dev;
1059
1060 mesh_dev = priv->mesh_dev;
1061 if (!mesh_dev)
1062 return;
1063
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;
Daniel Drake49fee692011-07-21 20:43:17 +01001071 kfree(mesh_dev->ieee80211_ptr);
Daniel Drake3db4f982011-07-20 17:53:50 +01001072 free_netdev(mesh_dev);
1073 lbs_deb_leave(LBS_DEB_MESH);
1074}
1075
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)
1082{
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;
1093}
1094
1095
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
Holger Schurigc7fe64c2009-11-25 13:10:49 +01001107
1108/***************************************************************************
1109 * Ethtool related
1110 */
1111
Daniel Drake3db4f982011-07-20 17:53:50 +01001112static const char * const mesh_stat_strings[] = {
Holger Schurigc7fe64c2009-11-25 13:10:49 +01001113 "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}