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