1162 lines · c
1// SPDX-License-Identifier: GPL-2.02#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt3 4#include <linux/delay.h>5#include <linux/etherdevice.h>6#include <linux/hardirq.h>7#include <linux/netdevice.h>8#include <linux/if_ether.h>9#include <linux/if_arp.h>10#include <linux/kthread.h>11#include <linux/kfifo.h>12#include <net/cfg80211.h>13 14#include "mesh.h"15#include "decl.h"16#include "cmd.h"17 18 19static int lbs_add_mesh(struct lbs_private *priv);20 21/***************************************************************************22 * Mesh command handling23 */24 25static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,26 struct cmd_ds_mesh_access *cmd)27{28 int ret;29 30 cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);31 cmd->hdr.size = cpu_to_le16(sizeof(*cmd));32 cmd->hdr.result = 0;33 34 cmd->action = cpu_to_le16(cmd_action);35 36 ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);37 38 return ret;39}40 41static int __lbs_mesh_config_send(struct lbs_private *priv,42 struct cmd_ds_mesh_config *cmd,43 uint16_t action, uint16_t type)44{45 int ret;46 u16 command = CMD_MESH_CONFIG_OLD;47 48 /*49 * Command id is 0xac for v10 FW along with mesh interface50 * id in bits 14-13-12.51 */52 if (priv->mesh_tlv == TLV_TYPE_MESH_ID)53 command = CMD_MESH_CONFIG |54 (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);55 56 cmd->hdr.command = cpu_to_le16(command);57 cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));58 cmd->hdr.result = 0;59 60 cmd->type = cpu_to_le16(type);61 cmd->action = cpu_to_le16(action);62 63 ret = lbs_cmd_with_response(priv, command, cmd);64 65 return ret;66}67 68static int lbs_mesh_config_send(struct lbs_private *priv,69 struct cmd_ds_mesh_config *cmd,70 uint16_t action, uint16_t type)71{72 int ret;73 74 if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))75 return -EOPNOTSUPP;76 77 ret = __lbs_mesh_config_send(priv, cmd, action, type);78 return ret;79}80 81/* This function is the CMD_MESH_CONFIG legacy function. It only handles the82 * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG83 * are all handled by preparing a struct cmd_ds_mesh_config and passing it to84 * lbs_mesh_config_send.85 */86static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,87 uint16_t chan)88{89 struct wireless_dev *mesh_wdev;90 struct cmd_ds_mesh_config cmd;91 struct mrvl_meshie *ie;92 93 memset(&cmd, 0, sizeof(cmd));94 cmd.channel = cpu_to_le16(chan);95 ie = (struct mrvl_meshie *)cmd.data;96 97 switch (action) {98 case CMD_ACT_MESH_CONFIG_START:99 ie->id = WLAN_EID_VENDOR_SPECIFIC;100 ie->val.oui[0] = 0x00;101 ie->val.oui[1] = 0x50;102 ie->val.oui[2] = 0x43;103 ie->val.type = MARVELL_MESH_IE_TYPE;104 ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;105 ie->val.version = MARVELL_MESH_IE_VERSION;106 ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;107 ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;108 ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;109 110 if (priv->mesh_dev) {111 mesh_wdev = priv->mesh_dev->ieee80211_ptr;112 ie->val.mesh_id_len = mesh_wdev->u.mesh.id_up_len;113 memcpy(ie->val.mesh_id, mesh_wdev->u.mesh.id,114 mesh_wdev->u.mesh.id_up_len);115 }116 117 ie->len = sizeof(struct mrvl_meshie_val) -118 IEEE80211_MAX_SSID_LEN + ie->val.mesh_id_len;119 120 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));121 break;122 case CMD_ACT_MESH_CONFIG_STOP:123 break;124 default:125 return -1;126 }127 lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n",128 action, priv->mesh_tlv, chan, ie->val.mesh_id_len,129 ie->val.mesh_id);130 131 return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);132}133 134int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)135{136 priv->mesh_channel = channel;137 return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);138}139 140static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)141{142 return priv->mesh_channel ?: 1;143}144 145/***************************************************************************146 * Mesh sysfs support147 */148 149/*150 * Attributes exported through sysfs151 */152 153/**154 * anycast_mask_show - Get function for sysfs attribute anycast_mask155 * @dev: the &struct device156 * @attr: device attributes157 * @buf: buffer where data will be returned158 */159static ssize_t anycast_mask_show(struct device *dev,160 struct device_attribute *attr, char *buf)161{162 struct lbs_private *priv = to_net_dev(dev)->ml_priv;163 struct cmd_ds_mesh_access mesh_access;164 int ret;165 166 memset(&mesh_access, 0, sizeof(mesh_access));167 168 ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);169 if (ret)170 return ret;171 172 return sysfs_emit(buf, "0x%X\n", le32_to_cpu(mesh_access.data[0]));173}174 175/**176 * anycast_mask_store - Set function for sysfs attribute anycast_mask177 * @dev: the &struct device178 * @attr: device attributes179 * @buf: buffer that contains new attribute value180 * @count: size of buffer181 */182static ssize_t anycast_mask_store(struct device *dev,183 struct device_attribute *attr,184 const char *buf, size_t count)185{186 struct lbs_private *priv = to_net_dev(dev)->ml_priv;187 struct cmd_ds_mesh_access mesh_access;188 uint32_t datum;189 int ret;190 191 ret = kstrtouint(buf, 16, &datum);192 if (ret)193 return ret;194 195 memset(&mesh_access, 0, sizeof(mesh_access));196 mesh_access.data[0] = cpu_to_le32(datum);197 198 ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);199 if (ret)200 return ret;201 202 return strlen(buf);203}204 205/**206 * prb_rsp_limit_show - Get function for sysfs attribute prb_rsp_limit207 * @dev: the &struct device208 * @attr: device attributes209 * @buf: buffer where data will be returned210 */211static ssize_t prb_rsp_limit_show(struct device *dev,212 struct device_attribute *attr, char *buf)213{214 struct lbs_private *priv = to_net_dev(dev)->ml_priv;215 struct cmd_ds_mesh_access mesh_access;216 int ret;217 u32 retry_limit;218 219 memset(&mesh_access, 0, sizeof(mesh_access));220 mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);221 222 ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,223 &mesh_access);224 if (ret)225 return ret;226 227 retry_limit = le32_to_cpu(mesh_access.data[1]);228 return sysfs_emit(buf, "%d\n", retry_limit);229}230 231/**232 * prb_rsp_limit_store - Set function for sysfs attribute prb_rsp_limit233 * @dev: the &struct device234 * @attr: device attributes235 * @buf: buffer that contains new attribute value236 * @count: size of buffer237 */238static ssize_t prb_rsp_limit_store(struct device *dev,239 struct device_attribute *attr,240 const char *buf, size_t count)241{242 struct lbs_private *priv = to_net_dev(dev)->ml_priv;243 struct cmd_ds_mesh_access mesh_access;244 int ret;245 unsigned long retry_limit;246 247 ret = kstrtoul(buf, 10, &retry_limit);248 if (ret)249 return ret;250 if (retry_limit > 15)251 return -ENOTSUPP;252 253 memset(&mesh_access, 0, sizeof(mesh_access));254 mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);255 mesh_access.data[1] = cpu_to_le32(retry_limit);256 257 ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,258 &mesh_access);259 if (ret)260 return ret;261 262 return strlen(buf);263}264 265/**266 * lbs_mesh_show - Get function for sysfs attribute mesh267 * @dev: the &struct device268 * @attr: device attributes269 * @buf: buffer where data will be returned270 */271static ssize_t lbs_mesh_show(struct device *dev,272 struct device_attribute *attr, char *buf)273{274 struct lbs_private *priv = to_net_dev(dev)->ml_priv;275 return sysfs_emit(buf, "0x%X\n", !!priv->mesh_dev);276}277 278/**279 * lbs_mesh_store - Set function for sysfs attribute mesh280 * @dev: the &struct device281 * @attr: device attributes282 * @buf: buffer that contains new attribute value283 * @count: size of buffer284 */285static ssize_t lbs_mesh_store(struct device *dev,286 struct device_attribute *attr,287 const char *buf, size_t count)288{289 struct lbs_private *priv = to_net_dev(dev)->ml_priv;290 int ret, enable;291 292 ret = kstrtoint(buf, 16, &enable);293 if (ret)294 return ret;295 296 enable = !!enable;297 if (enable == !!priv->mesh_dev)298 return count;299 300 if (enable)301 lbs_add_mesh(priv);302 else303 lbs_remove_mesh(priv);304 305 return count;306}307 308/*309 * lbs_mesh attribute to be exported per ethX interface310 * through sysfs (/sys/class/net/ethX/lbs_mesh)311 */312static DEVICE_ATTR_RW(lbs_mesh);313 314/*315 * anycast_mask attribute to be exported per mshX interface316 * through sysfs (/sys/class/net/mshX/anycast_mask)317 */318static DEVICE_ATTR_RW(anycast_mask);319 320/*321 * prb_rsp_limit attribute to be exported per mshX interface322 * through sysfs (/sys/class/net/mshX/prb_rsp_limit)323 */324static DEVICE_ATTR_RW(prb_rsp_limit);325 326static struct attribute *lbs_mesh_sysfs_entries[] = {327 &dev_attr_anycast_mask.attr,328 &dev_attr_prb_rsp_limit.attr,329 NULL,330};331 332static const struct attribute_group lbs_mesh_attr_group = {333 .attrs = lbs_mesh_sysfs_entries,334};335 336 337/***************************************************************************338 * Persistent configuration support339 */340 341static int mesh_get_default_parameters(struct device *dev,342 struct mrvl_mesh_defaults *defs)343{344 struct lbs_private *priv = to_net_dev(dev)->ml_priv;345 struct cmd_ds_mesh_config cmd;346 int ret;347 348 memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));349 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,350 CMD_TYPE_MESH_GET_DEFAULTS);351 352 if (ret)353 return -EOPNOTSUPP;354 355 memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));356 357 return 0;358}359 360/**361 * bootflag_show - Get function for sysfs attribute bootflag362 * @dev: the &struct device363 * @attr: device attributes364 * @buf: buffer where data will be returned365 */366static ssize_t bootflag_show(struct device *dev,367 struct device_attribute *attr, char *buf)368{369 struct mrvl_mesh_defaults defs;370 int ret;371 372 ret = mesh_get_default_parameters(dev, &defs);373 374 if (ret)375 return ret;376 377 return sysfs_emit(buf, "%d\n", le32_to_cpu(defs.bootflag));378}379 380/**381 * bootflag_store - Set function for sysfs attribute bootflag382 * @dev: the &struct device383 * @attr: device attributes384 * @buf: buffer that contains new attribute value385 * @count: size of buffer386 */387static ssize_t bootflag_store(struct device *dev, struct device_attribute *attr,388 const char *buf, size_t count)389{390 struct lbs_private *priv = to_net_dev(dev)->ml_priv;391 struct cmd_ds_mesh_config cmd;392 uint32_t datum;393 int ret;394 395 ret = kstrtouint(buf, 10, &datum);396 if (ret)397 return ret;398 if (datum > 1)399 return -EINVAL;400 401 memset(&cmd, 0, sizeof(cmd));402 *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);403 cmd.length = cpu_to_le16(sizeof(uint32_t));404 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,405 CMD_TYPE_MESH_SET_BOOTFLAG);406 if (ret)407 return ret;408 409 return strlen(buf);410}411 412/**413 * boottime_show - Get function for sysfs attribute boottime414 * @dev: the &struct device415 * @attr: device attributes416 * @buf: buffer where data will be returned417 */418static ssize_t boottime_show(struct device *dev,419 struct device_attribute *attr, char *buf)420{421 struct mrvl_mesh_defaults defs;422 int ret;423 424 ret = mesh_get_default_parameters(dev, &defs);425 426 if (ret)427 return ret;428 429 return sysfs_emit(buf, "%d\n", defs.boottime);430}431 432/**433 * boottime_store - Set function for sysfs attribute boottime434 * @dev: the &struct device435 * @attr: device attributes436 * @buf: buffer that contains new attribute value437 * @count: size of buffer438 */439static ssize_t boottime_store(struct device *dev,440 struct device_attribute *attr,441 const char *buf, size_t count)442{443 struct lbs_private *priv = to_net_dev(dev)->ml_priv;444 struct cmd_ds_mesh_config cmd;445 uint32_t datum;446 int ret;447 448 ret = kstrtouint(buf, 10, &datum);449 if (ret)450 return ret;451 if (datum > 255)452 return -EINVAL;453 454 memset(&cmd, 0, sizeof(cmd));455 456 /* A too small boot time will result in the device booting into457 * standalone (no-host) mode before the host can take control of it,458 * so the change will be hard to revert. This may be a desired459 * feature (e.g to configure a very fast boot time for devices that460 * will not be attached to a host), but dangerous. So I'm enforcing a461 * lower limit of 20 seconds: remove and recompile the driver if this462 * does not work for you.463 */464 datum = (datum < 20) ? 20 : datum;465 cmd.data[0] = datum;466 cmd.length = cpu_to_le16(sizeof(uint8_t));467 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,468 CMD_TYPE_MESH_SET_BOOTTIME);469 if (ret)470 return ret;471 472 return strlen(buf);473}474 475/**476 * channel_show - Get function for sysfs attribute channel477 * @dev: the &struct device478 * @attr: device attributes479 * @buf: buffer where data will be returned480 */481static ssize_t channel_show(struct device *dev,482 struct device_attribute *attr, char *buf)483{484 struct mrvl_mesh_defaults defs;485 int ret;486 487 ret = mesh_get_default_parameters(dev, &defs);488 489 if (ret)490 return ret;491 492 return sysfs_emit(buf, "%d\n", le16_to_cpu(defs.channel));493}494 495/**496 * channel_store - Set function for sysfs attribute channel497 * @dev: the &struct device498 * @attr: device attributes499 * @buf: buffer that contains new attribute value500 * @count: size of buffer501 */502static ssize_t channel_store(struct device *dev, struct device_attribute *attr,503 const char *buf, size_t count)504{505 struct lbs_private *priv = to_net_dev(dev)->ml_priv;506 struct cmd_ds_mesh_config cmd;507 uint32_t datum;508 int ret;509 510 ret = kstrtouint(buf, 10, &datum);511 if (ret)512 return ret;513 if (datum < 1 || datum > 11)514 return -EINVAL;515 516 memset(&cmd, 0, sizeof(cmd));517 *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);518 cmd.length = cpu_to_le16(sizeof(uint16_t));519 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,520 CMD_TYPE_MESH_SET_DEF_CHANNEL);521 if (ret)522 return ret;523 524 return strlen(buf);525}526 527/**528 * mesh_id_show - Get function for sysfs attribute mesh_id529 * @dev: the &struct device530 * @attr: device attributes531 * @buf: buffer where data will be returned532 */533static ssize_t mesh_id_show(struct device *dev, struct device_attribute *attr,534 char *buf)535{536 struct mrvl_mesh_defaults defs;537 int ret;538 539 ret = mesh_get_default_parameters(dev, &defs);540 541 if (ret)542 return ret;543 544 if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {545 dev_err(dev, "inconsistent mesh ID length\n");546 defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;547 }548 549 memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);550 buf[defs.meshie.val.mesh_id_len] = '\n';551 buf[defs.meshie.val.mesh_id_len + 1] = '\0';552 553 return defs.meshie.val.mesh_id_len + 1;554}555 556/**557 * mesh_id_store - Set function for sysfs attribute mesh_id558 * @dev: the &struct device559 * @attr: device attributes560 * @buf: buffer that contains new attribute value561 * @count: size of buffer562 */563static ssize_t mesh_id_store(struct device *dev, struct device_attribute *attr,564 const char *buf, size_t count)565{566 struct cmd_ds_mesh_config cmd;567 struct mrvl_mesh_defaults defs;568 struct mrvl_meshie *ie;569 struct lbs_private *priv = to_net_dev(dev)->ml_priv;570 int len;571 int ret;572 573 if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)574 return -EINVAL;575 576 memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));577 ie = (struct mrvl_meshie *) &cmd.data[0];578 579 /* fetch all other Information Element parameters */580 ret = mesh_get_default_parameters(dev, &defs);581 582 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));583 584 /* transfer IE elements */585 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));586 587 len = count - 1;588 memcpy(ie->val.mesh_id, buf, len);589 /* SSID len */590 ie->val.mesh_id_len = len;591 /* IE len */592 ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;593 594 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,595 CMD_TYPE_MESH_SET_MESH_IE);596 if (ret)597 return ret;598 599 return strlen(buf);600}601 602/**603 * protocol_id_show - Get function for sysfs attribute protocol_id604 * @dev: the &struct device605 * @attr: device attributes606 * @buf: buffer where data will be returned607 */608static ssize_t protocol_id_show(struct device *dev,609 struct device_attribute *attr,610 char *buf)611{612 struct mrvl_mesh_defaults defs;613 int ret;614 615 ret = mesh_get_default_parameters(dev, &defs);616 617 if (ret)618 return ret;619 620 return sysfs_emit(buf, "%d\n", defs.meshie.val.active_protocol_id);621}622 623/**624 * protocol_id_store - Set function for sysfs attribute protocol_id625 * @dev: the &struct device626 * @attr: device attributes627 * @buf: buffer that contains new attribute value628 * @count: size of buffer629 */630static ssize_t protocol_id_store(struct device *dev,631 struct device_attribute *attr,632 const char *buf, size_t count)633{634 struct cmd_ds_mesh_config cmd;635 struct mrvl_mesh_defaults defs;636 struct mrvl_meshie *ie;637 struct lbs_private *priv = to_net_dev(dev)->ml_priv;638 uint32_t datum;639 int ret;640 641 ret = kstrtouint(buf, 10, &datum);642 if (ret)643 return ret;644 if (datum > 255)645 return -EINVAL;646 647 memset(&cmd, 0, sizeof(cmd));648 649 /* fetch all other Information Element parameters */650 ret = mesh_get_default_parameters(dev, &defs);651 652 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));653 654 /* transfer IE elements */655 ie = (struct mrvl_meshie *) &cmd.data[0];656 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));657 /* update protocol id */658 ie->val.active_protocol_id = datum;659 660 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,661 CMD_TYPE_MESH_SET_MESH_IE);662 if (ret)663 return ret;664 665 return strlen(buf);666}667 668/**669 * metric_id_show - Get function for sysfs attribute metric_id670 * @dev: the &struct device671 * @attr: device attributes672 * @buf: buffer where data will be returned673 */674static ssize_t metric_id_show(struct device *dev,675 struct device_attribute *attr, char *buf)676{677 struct mrvl_mesh_defaults defs;678 int ret;679 680 ret = mesh_get_default_parameters(dev, &defs);681 682 if (ret)683 return ret;684 685 return sysfs_emit(buf, "%d\n", defs.meshie.val.active_metric_id);686}687 688/**689 * metric_id_store - Set function for sysfs attribute metric_id690 * @dev: the &struct device691 * @attr: device attributes692 * @buf: buffer that contains new attribute value693 * @count: size of buffer694 */695static ssize_t metric_id_store(struct device *dev,696 struct device_attribute *attr,697 const char *buf, size_t count)698{699 struct cmd_ds_mesh_config cmd;700 struct mrvl_mesh_defaults defs;701 struct mrvl_meshie *ie;702 struct lbs_private *priv = to_net_dev(dev)->ml_priv;703 uint32_t datum;704 int ret;705 706 memset(&cmd, 0, sizeof(cmd));707 ret = sscanf(buf, "%d", &datum);708 if ((ret != 1) || (datum > 255))709 return -EINVAL;710 711 /* fetch all other Information Element parameters */712 ret = mesh_get_default_parameters(dev, &defs);713 714 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));715 716 /* transfer IE elements */717 ie = (struct mrvl_meshie *) &cmd.data[0];718 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));719 /* update metric id */720 ie->val.active_metric_id = datum;721 722 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,723 CMD_TYPE_MESH_SET_MESH_IE);724 if (ret)725 return ret;726 727 return strlen(buf);728}729 730/**731 * capability_show - Get function for sysfs attribute capability732 * @dev: the &struct device733 * @attr: device attributes734 * @buf: buffer where data will be returned735 */736static ssize_t capability_show(struct device *dev,737 struct device_attribute *attr, char *buf)738{739 struct mrvl_mesh_defaults defs;740 int ret;741 742 ret = mesh_get_default_parameters(dev, &defs);743 744 if (ret)745 return ret;746 747 return sysfs_emit(buf, "%d\n", defs.meshie.val.mesh_capability);748}749 750/**751 * capability_store - Set function for sysfs attribute capability752 * @dev: the &struct device753 * @attr: device attributes754 * @buf: buffer that contains new attribute value755 * @count: size of buffer756 */757static ssize_t capability_store(struct device *dev,758 struct device_attribute *attr,759 const char *buf, size_t count)760{761 struct cmd_ds_mesh_config cmd;762 struct mrvl_mesh_defaults defs;763 struct mrvl_meshie *ie;764 struct lbs_private *priv = to_net_dev(dev)->ml_priv;765 uint32_t datum;766 int ret;767 768 memset(&cmd, 0, sizeof(cmd));769 ret = sscanf(buf, "%d", &datum);770 if ((ret != 1) || (datum > 255))771 return -EINVAL;772 773 /* fetch all other Information Element parameters */774 ret = mesh_get_default_parameters(dev, &defs);775 776 cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));777 778 /* transfer IE elements */779 ie = (struct mrvl_meshie *) &cmd.data[0];780 memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));781 /* update value */782 ie->val.mesh_capability = datum;783 784 ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,785 CMD_TYPE_MESH_SET_MESH_IE);786 if (ret)787 return ret;788 789 return strlen(buf);790}791 792 793static DEVICE_ATTR_RW(bootflag);794static DEVICE_ATTR_RW(boottime);795static DEVICE_ATTR_RW(channel);796static DEVICE_ATTR_RW(mesh_id);797static DEVICE_ATTR_RW(protocol_id);798static DEVICE_ATTR_RW(metric_id);799static DEVICE_ATTR_RW(capability);800 801static struct attribute *boot_opts_attrs[] = {802 &dev_attr_bootflag.attr,803 &dev_attr_boottime.attr,804 &dev_attr_channel.attr,805 NULL806};807 808static const struct attribute_group boot_opts_group = {809 .name = "boot_options",810 .attrs = boot_opts_attrs,811};812 813static struct attribute *mesh_ie_attrs[] = {814 &dev_attr_mesh_id.attr,815 &dev_attr_protocol_id.attr,816 &dev_attr_metric_id.attr,817 &dev_attr_capability.attr,818 NULL819};820 821static const struct attribute_group mesh_ie_group = {822 .name = "mesh_ie",823 .attrs = mesh_ie_attrs,824};825 826 827/***************************************************************************828 * Initializing and starting, stopping mesh829 */830 831/*832 * Check mesh FW version and appropriately send the mesh start833 * command834 */835void lbs_init_mesh(struct lbs_private *priv)836{837 /* Determine mesh_fw_ver from fwrelease and fwcapinfo */838 /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */839 /* 5.110.22 have mesh command with 0xa3 command id */840 /* 10.0.0.p0 FW brings in mesh config command with different id */841 /* Check FW version MSB and initialize mesh_fw_ver */842 if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {843 /* Enable mesh, if supported, and work out which TLV it uses.844 0x100 + 291 is an unofficial value used in 5.110.20.pXX845 0x100 + 37 is the official value used in 5.110.21.pXX846 but we check them in that order because 20.pXX doesn't847 give an error -- it just silently fails. */848 849 /* 5.110.20.pXX firmware will fail the command if the channel850 doesn't match the existing channel. But only if the TLV851 is correct. If the channel is wrong, _BOTH_ versions will852 give an error to 0x100+291, and allow 0x100+37 to succeed.853 It's just that 5.110.20.pXX will not have done anything854 useful */855 856 priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;857 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {858 priv->mesh_tlv = TLV_TYPE_MESH_ID;859 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))860 priv->mesh_tlv = 0;861 }862 } else863 if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&864 (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {865 /* 10.0.0.pXX new firmwares should succeed with TLV866 * 0x100+37; Do not invoke command with old TLV.867 */868 priv->mesh_tlv = TLV_TYPE_MESH_ID;869 if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))870 priv->mesh_tlv = 0;871 }872 873 /* Stop meshing until interface is brought up */874 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);875}876 877void lbs_start_mesh(struct lbs_private *priv)878{879 lbs_add_mesh(priv);880 881 if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))882 netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");883}884 885int lbs_deinit_mesh(struct lbs_private *priv)886{887 struct net_device *dev = priv->dev;888 int ret = 0;889 890 if (priv->mesh_tlv) {891 device_remove_file(&dev->dev, &dev_attr_lbs_mesh);892 ret = 1;893 }894 895 return ret;896}897 898 899/**900 * lbs_mesh_stop - close the mshX interface901 *902 * @dev: A pointer to &net_device structure903 * returns: 0904 */905static int lbs_mesh_stop(struct net_device *dev)906{907 struct lbs_private *priv = dev->ml_priv;908 909 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,910 lbs_mesh_get_channel(priv));911 912 spin_lock_irq(&priv->driver_lock);913 914 netif_stop_queue(dev);915 netif_carrier_off(dev);916 917 spin_unlock_irq(&priv->driver_lock);918 919 lbs_update_mcast(priv);920 if (!lbs_iface_active(priv))921 lbs_stop_iface(priv);922 923 return 0;924}925 926/**927 * lbs_mesh_dev_open - open the mshX interface928 *929 * @dev: A pointer to &net_device structure930 * returns: 0 or -EBUSY if monitor mode active931 */932static int lbs_mesh_dev_open(struct net_device *dev)933{934 struct lbs_private *priv = dev->ml_priv;935 int ret = 0;936 937 if (!priv->iface_running) {938 ret = lbs_start_iface(priv);939 if (ret)940 goto out;941 }942 943 spin_lock_irq(&priv->driver_lock);944 945 if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {946 ret = -EBUSY;947 spin_unlock_irq(&priv->driver_lock);948 goto out;949 }950 951 netif_carrier_on(dev);952 953 if (!priv->tx_pending_len)954 netif_wake_queue(dev);955 956 spin_unlock_irq(&priv->driver_lock);957 958 ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,959 lbs_mesh_get_channel(priv));960 961out:962 return ret;963}964 965static const struct net_device_ops mesh_netdev_ops = {966 .ndo_open = lbs_mesh_dev_open,967 .ndo_stop = lbs_mesh_stop,968 .ndo_start_xmit = lbs_hard_start_xmit,969 .ndo_set_mac_address = lbs_set_mac_address,970 .ndo_set_rx_mode = lbs_set_multicast_list,971};972 973/**974 * lbs_add_mesh - add mshX interface975 *976 * @priv: A pointer to the &struct lbs_private structure977 * returns: 0 if successful, -X otherwise978 */979static int lbs_add_mesh(struct lbs_private *priv)980{981 struct net_device *mesh_dev = NULL;982 struct wireless_dev *mesh_wdev;983 int ret = 0;984 985 /* Allocate a virtual mesh device */986 mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);987 if (!mesh_wdev) {988 lbs_deb_mesh("init mshX wireless device failed\n");989 ret = -ENOMEM;990 goto done;991 }992 993 mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup);994 if (!mesh_dev) {995 lbs_deb_mesh("init mshX device failed\n");996 ret = -ENOMEM;997 goto err_free_wdev;998 }999 1000 mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;1001 mesh_wdev->wiphy = priv->wdev->wiphy;1002 1003 if (priv->mesh_tlv) {1004 sprintf(mesh_wdev->u.mesh.id, "mesh");1005 mesh_wdev->u.mesh.id_up_len = 4;1006 }1007 1008 mesh_wdev->netdev = mesh_dev;1009 1010 mesh_dev->ml_priv = priv;1011 mesh_dev->ieee80211_ptr = mesh_wdev;1012 priv->mesh_dev = mesh_dev;1013 1014 mesh_dev->netdev_ops = &mesh_netdev_ops;1015 mesh_dev->ethtool_ops = &lbs_ethtool_ops;1016 eth_hw_addr_inherit(mesh_dev, priv->dev);1017 1018 SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);1019 1020 mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;1021 mesh_dev->sysfs_groups[0] = &lbs_mesh_attr_group;1022 mesh_dev->sysfs_groups[1] = &boot_opts_group;1023 mesh_dev->sysfs_groups[2] = &mesh_ie_group;1024 1025 /* Register virtual mesh interface */1026 ret = register_netdev(mesh_dev);1027 if (ret) {1028 pr_err("cannot register mshX virtual interface\n");1029 goto err_free_netdev;1030 }1031 1032 /* Everything successful */1033 ret = 0;1034 goto done;1035 1036err_free_netdev:1037 free_netdev(mesh_dev);1038 1039err_free_wdev:1040 kfree(mesh_wdev);1041 1042done:1043 return ret;1044}1045 1046void lbs_remove_mesh(struct lbs_private *priv)1047{1048 struct net_device *mesh_dev;1049 1050 mesh_dev = priv->mesh_dev;1051 if (!mesh_dev)1052 return;1053 1054 netif_stop_queue(mesh_dev);1055 netif_carrier_off(mesh_dev);1056 unregister_netdev(mesh_dev);1057 priv->mesh_dev = NULL;1058 kfree(mesh_dev->ieee80211_ptr);1059 free_netdev(mesh_dev);1060}1061 1062 1063/***************************************************************************1064 * Sending and receiving1065 */1066struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,1067 struct net_device *dev, struct rxpd *rxpd)1068{1069 if (priv->mesh_dev) {1070 if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {1071 if (rxpd->rx_control & RxPD_MESH_FRAME)1072 dev = priv->mesh_dev;1073 } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {1074 if (rxpd->u.bss.bss_num == MESH_IFACE_ID)1075 dev = priv->mesh_dev;1076 }1077 }1078 return dev;1079}1080 1081 1082void lbs_mesh_set_txpd(struct lbs_private *priv,1083 struct net_device *dev, struct txpd *txpd)1084{1085 if (dev == priv->mesh_dev) {1086 if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)1087 txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);1088 else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)1089 txpd->u.bss.bss_num = MESH_IFACE_ID;1090 }1091}1092 1093 1094/***************************************************************************1095 * Ethtool related1096 */1097 1098static const char mesh_stat_strings[MESH_STATS_NUM][ETH_GSTRING_LEN] = {1099 "drop_duplicate_bcast",1100 "drop_ttl_zero",1101 "drop_no_fwd_route",1102 "drop_no_buffers",1103 "fwded_unicast_cnt",1104 "fwded_bcast_cnt",1105 "drop_blind_table",1106 "tx_failed_cnt"1107};1108 1109void lbs_mesh_ethtool_get_stats(struct net_device *dev,1110 struct ethtool_stats *stats, uint64_t *data)1111{1112 struct lbs_private *priv = dev->ml_priv;1113 struct cmd_ds_mesh_access mesh_access;1114 int ret;1115 1116 /* Get Mesh Statistics */1117 ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);1118 1119 if (ret) {1120 memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));1121 return;1122 }1123 1124 priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);1125 priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);1126 priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);1127 priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);1128 priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);1129 priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);1130 priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);1131 priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);1132 1133 data[0] = priv->mstats.fwd_drop_rbt;1134 data[1] = priv->mstats.fwd_drop_ttl;1135 data[2] = priv->mstats.fwd_drop_noroute;1136 data[3] = priv->mstats.fwd_drop_nobuf;1137 data[4] = priv->mstats.fwd_unicast_cnt;1138 data[5] = priv->mstats.fwd_bcast_cnt;1139 data[6] = priv->mstats.drop_blind;1140 data[7] = priv->mstats.tx_failed_cnt;1141}1142 1143int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)1144{1145 struct lbs_private *priv = dev->ml_priv;1146 1147 if (sset == ETH_SS_STATS && dev == priv->mesh_dev)1148 return MESH_STATS_NUM;1149 1150 return -EOPNOTSUPP;1151}1152 1153void lbs_mesh_ethtool_get_strings(struct net_device *dev,1154 uint32_t stringset, uint8_t *s)1155{1156 switch (stringset) {1157 case ETH_SS_STATS:1158 memcpy(s, mesh_stat_strings, sizeof(mesh_stat_strings));1159 break;1160 }1161}1162