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