162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/delay.h>
562306a36Sopenharmony_ci#include <linux/etherdevice.h>
662306a36Sopenharmony_ci#include <linux/hardirq.h>
762306a36Sopenharmony_ci#include <linux/netdevice.h>
862306a36Sopenharmony_ci#include <linux/if_ether.h>
962306a36Sopenharmony_ci#include <linux/if_arp.h>
1062306a36Sopenharmony_ci#include <linux/kthread.h>
1162306a36Sopenharmony_ci#include <linux/kfifo.h>
1262306a36Sopenharmony_ci#include <net/cfg80211.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "mesh.h"
1562306a36Sopenharmony_ci#include "decl.h"
1662306a36Sopenharmony_ci#include "cmd.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int lbs_add_mesh(struct lbs_private *priv);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/***************************************************************************
2262306a36Sopenharmony_ci * Mesh command handling
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
2662306a36Sopenharmony_ci		    struct cmd_ds_mesh_access *cmd)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	int ret;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
3162306a36Sopenharmony_ci	cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
3262306a36Sopenharmony_ci	cmd->hdr.result = 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	cmd->action = cpu_to_le16(cmd_action);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return ret;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int __lbs_mesh_config_send(struct lbs_private *priv,
4262306a36Sopenharmony_ci				  struct cmd_ds_mesh_config *cmd,
4362306a36Sopenharmony_ci				  uint16_t action, uint16_t type)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	int ret;
4662306a36Sopenharmony_ci	u16 command = CMD_MESH_CONFIG_OLD;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/*
4962306a36Sopenharmony_ci	 * Command id is 0xac for v10 FW along with mesh interface
5062306a36Sopenharmony_ci	 * id in bits 14-13-12.
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci	if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
5362306a36Sopenharmony_ci		command = CMD_MESH_CONFIG |
5462306a36Sopenharmony_ci			  (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	cmd->hdr.command = cpu_to_le16(command);
5762306a36Sopenharmony_ci	cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
5862306a36Sopenharmony_ci	cmd->hdr.result = 0;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	cmd->type = cpu_to_le16(type);
6162306a36Sopenharmony_ci	cmd->action = cpu_to_le16(action);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = lbs_cmd_with_response(priv, command, cmd);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return ret;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int lbs_mesh_config_send(struct lbs_private *priv,
6962306a36Sopenharmony_ci			 struct cmd_ds_mesh_config *cmd,
7062306a36Sopenharmony_ci			 uint16_t action, uint16_t type)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	int ret;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
7562306a36Sopenharmony_ci		return -EOPNOTSUPP;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	ret = __lbs_mesh_config_send(priv, cmd, action, type);
7862306a36Sopenharmony_ci	return ret;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* This function is the CMD_MESH_CONFIG legacy function.  It only handles the
8262306a36Sopenharmony_ci * START and STOP actions.  The extended actions supported by CMD_MESH_CONFIG
8362306a36Sopenharmony_ci * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
8462306a36Sopenharmony_ci * lbs_mesh_config_send.
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
8762306a36Sopenharmony_ci		uint16_t chan)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct wireless_dev *mesh_wdev;
9062306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
9162306a36Sopenharmony_ci	struct mrvl_meshie *ie;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
9462306a36Sopenharmony_ci	cmd.channel = cpu_to_le16(chan);
9562306a36Sopenharmony_ci	ie = (struct mrvl_meshie *)cmd.data;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	switch (action) {
9862306a36Sopenharmony_ci	case CMD_ACT_MESH_CONFIG_START:
9962306a36Sopenharmony_ci		ie->id = WLAN_EID_VENDOR_SPECIFIC;
10062306a36Sopenharmony_ci		ie->val.oui[0] = 0x00;
10162306a36Sopenharmony_ci		ie->val.oui[1] = 0x50;
10262306a36Sopenharmony_ci		ie->val.oui[2] = 0x43;
10362306a36Sopenharmony_ci		ie->val.type = MARVELL_MESH_IE_TYPE;
10462306a36Sopenharmony_ci		ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
10562306a36Sopenharmony_ci		ie->val.version = MARVELL_MESH_IE_VERSION;
10662306a36Sopenharmony_ci		ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
10762306a36Sopenharmony_ci		ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
10862306a36Sopenharmony_ci		ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		if (priv->mesh_dev) {
11162306a36Sopenharmony_ci			mesh_wdev = priv->mesh_dev->ieee80211_ptr;
11262306a36Sopenharmony_ci			ie->val.mesh_id_len = mesh_wdev->u.mesh.id_up_len;
11362306a36Sopenharmony_ci			memcpy(ie->val.mesh_id, mesh_wdev->u.mesh.id,
11462306a36Sopenharmony_ci						mesh_wdev->u.mesh.id_up_len);
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		ie->len = sizeof(struct mrvl_meshie_val) -
11862306a36Sopenharmony_ci			IEEE80211_MAX_SSID_LEN + ie->val.mesh_id_len;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case CMD_ACT_MESH_CONFIG_STOP:
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci	default:
12562306a36Sopenharmony_ci		return -1;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n",
12862306a36Sopenharmony_ci		    action, priv->mesh_tlv, chan, ie->val.mesh_id_len,
12962306a36Sopenharmony_ci		    ie->val.mesh_id);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciint lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	priv->mesh_channel = channel;
13762306a36Sopenharmony_ci	return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	return priv->mesh_channel ?: 1;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/***************************************************************************
14662306a36Sopenharmony_ci * Mesh sysfs support
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Attributes exported through sysfs
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * anycast_mask_show - Get function for sysfs attribute anycast_mask
15562306a36Sopenharmony_ci * @dev: the &struct device
15662306a36Sopenharmony_ci * @attr: device attributes
15762306a36Sopenharmony_ci * @buf: buffer where data will be returned
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_cistatic ssize_t anycast_mask_show(struct device *dev,
16062306a36Sopenharmony_ci				 struct device_attribute *attr, char *buf)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
16362306a36Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
16462306a36Sopenharmony_ci	int ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
16962306a36Sopenharmony_ci	if (ret)
17062306a36Sopenharmony_ci		return ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * anycast_mask_store - Set function for sysfs attribute anycast_mask
17762306a36Sopenharmony_ci * @dev: the &struct device
17862306a36Sopenharmony_ci * @attr: device attributes
17962306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
18062306a36Sopenharmony_ci * @count: size of buffer
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_cistatic ssize_t anycast_mask_store(struct device *dev,
18362306a36Sopenharmony_ci				  struct device_attribute *attr,
18462306a36Sopenharmony_ci				  const char *buf, size_t count)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
18762306a36Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
18862306a36Sopenharmony_ci	uint32_t datum;
18962306a36Sopenharmony_ci	int ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = kstrtouint(buf, 16, &datum);
19262306a36Sopenharmony_ci	if (ret)
19362306a36Sopenharmony_ci		return ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
19662306a36Sopenharmony_ci	mesh_access.data[0] = cpu_to_le32(datum);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
19962306a36Sopenharmony_ci	if (ret)
20062306a36Sopenharmony_ci		return ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return strlen(buf);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/**
20662306a36Sopenharmony_ci * prb_rsp_limit_show - Get function for sysfs attribute prb_rsp_limit
20762306a36Sopenharmony_ci * @dev: the &struct device
20862306a36Sopenharmony_ci * @attr: device attributes
20962306a36Sopenharmony_ci * @buf: buffer where data will be returned
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_cistatic ssize_t prb_rsp_limit_show(struct device *dev,
21262306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
21562306a36Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
21662306a36Sopenharmony_ci	int ret;
21762306a36Sopenharmony_ci	u32 retry_limit;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
22062306a36Sopenharmony_ci	mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
22362306a36Sopenharmony_ci			&mesh_access);
22462306a36Sopenharmony_ci	if (ret)
22562306a36Sopenharmony_ci		return ret;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	retry_limit = le32_to_cpu(mesh_access.data[1]);
22862306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", retry_limit);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/**
23262306a36Sopenharmony_ci * prb_rsp_limit_store - Set function for sysfs attribute prb_rsp_limit
23362306a36Sopenharmony_ci * @dev: the &struct device
23462306a36Sopenharmony_ci * @attr: device attributes
23562306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
23662306a36Sopenharmony_ci * @count: size of buffer
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cistatic ssize_t prb_rsp_limit_store(struct device *dev,
23962306a36Sopenharmony_ci				   struct device_attribute *attr,
24062306a36Sopenharmony_ci				   const char *buf, size_t count)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
24362306a36Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
24462306a36Sopenharmony_ci	int ret;
24562306a36Sopenharmony_ci	unsigned long retry_limit;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ret = kstrtoul(buf, 10, &retry_limit);
24862306a36Sopenharmony_ci	if (ret)
24962306a36Sopenharmony_ci		return ret;
25062306a36Sopenharmony_ci	if (retry_limit > 15)
25162306a36Sopenharmony_ci		return -ENOTSUPP;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
25462306a36Sopenharmony_ci	mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
25562306a36Sopenharmony_ci	mesh_access.data[1] = cpu_to_le32(retry_limit);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
25862306a36Sopenharmony_ci			&mesh_access);
25962306a36Sopenharmony_ci	if (ret)
26062306a36Sopenharmony_ci		return ret;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return strlen(buf);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/**
26662306a36Sopenharmony_ci * lbs_mesh_show - Get function for sysfs attribute mesh
26762306a36Sopenharmony_ci * @dev: the &struct device
26862306a36Sopenharmony_ci * @attr: device attributes
26962306a36Sopenharmony_ci * @buf: buffer where data will be returned
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_cistatic ssize_t lbs_mesh_show(struct device *dev,
27262306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
27562306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%X\n", !!priv->mesh_dev);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/**
27962306a36Sopenharmony_ci * lbs_mesh_store - Set function for sysfs attribute mesh
28062306a36Sopenharmony_ci * @dev: the &struct device
28162306a36Sopenharmony_ci * @attr: device attributes
28262306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
28362306a36Sopenharmony_ci * @count: size of buffer
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic ssize_t lbs_mesh_store(struct device *dev,
28662306a36Sopenharmony_ci			      struct device_attribute *attr,
28762306a36Sopenharmony_ci			      const char *buf, size_t count)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
29062306a36Sopenharmony_ci	int ret, enable;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ret = kstrtoint(buf, 16, &enable);
29362306a36Sopenharmony_ci	if (ret)
29462306a36Sopenharmony_ci		return ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	enable = !!enable;
29762306a36Sopenharmony_ci	if (enable == !!priv->mesh_dev)
29862306a36Sopenharmony_ci		return count;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (enable)
30162306a36Sopenharmony_ci		lbs_add_mesh(priv);
30262306a36Sopenharmony_ci	else
30362306a36Sopenharmony_ci		lbs_remove_mesh(priv);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return count;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/*
30962306a36Sopenharmony_ci * lbs_mesh attribute to be exported per ethX interface
31062306a36Sopenharmony_ci * through sysfs (/sys/class/net/ethX/lbs_mesh)
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(lbs_mesh);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/*
31562306a36Sopenharmony_ci * anycast_mask attribute to be exported per mshX interface
31662306a36Sopenharmony_ci * through sysfs (/sys/class/net/mshX/anycast_mask)
31762306a36Sopenharmony_ci */
31862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(anycast_mask);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/*
32162306a36Sopenharmony_ci * prb_rsp_limit attribute to be exported per mshX interface
32262306a36Sopenharmony_ci * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(prb_rsp_limit);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic struct attribute *lbs_mesh_sysfs_entries[] = {
32762306a36Sopenharmony_ci	&dev_attr_anycast_mask.attr,
32862306a36Sopenharmony_ci	&dev_attr_prb_rsp_limit.attr,
32962306a36Sopenharmony_ci	NULL,
33062306a36Sopenharmony_ci};
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic const struct attribute_group lbs_mesh_attr_group = {
33362306a36Sopenharmony_ci	.attrs = lbs_mesh_sysfs_entries,
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/***************************************************************************
33862306a36Sopenharmony_ci * Persistent configuration support
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int mesh_get_default_parameters(struct device *dev,
34262306a36Sopenharmony_ci				       struct mrvl_mesh_defaults *defs)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
34562306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
34662306a36Sopenharmony_ci	int ret;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
34962306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
35062306a36Sopenharmony_ci				   CMD_TYPE_MESH_GET_DEFAULTS);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (ret)
35362306a36Sopenharmony_ci		return -EOPNOTSUPP;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/**
36162306a36Sopenharmony_ci * bootflag_show - Get function for sysfs attribute bootflag
36262306a36Sopenharmony_ci * @dev: the &struct device
36362306a36Sopenharmony_ci * @attr: device attributes
36462306a36Sopenharmony_ci * @buf: buffer where data will be returned
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_cistatic ssize_t bootflag_show(struct device *dev,
36762306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
37062306a36Sopenharmony_ci	int ret;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (ret)
37562306a36Sopenharmony_ci		return ret;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", le32_to_cpu(defs.bootflag));
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/**
38162306a36Sopenharmony_ci * bootflag_store - Set function for sysfs attribute bootflag
38262306a36Sopenharmony_ci * @dev: the &struct device
38362306a36Sopenharmony_ci * @attr: device attributes
38462306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
38562306a36Sopenharmony_ci * @count: size of buffer
38662306a36Sopenharmony_ci */
38762306a36Sopenharmony_cistatic ssize_t bootflag_store(struct device *dev, struct device_attribute *attr,
38862306a36Sopenharmony_ci			      const char *buf, size_t count)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
39162306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
39262306a36Sopenharmony_ci	uint32_t datum;
39362306a36Sopenharmony_ci	int ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, &datum);
39662306a36Sopenharmony_ci	if (ret)
39762306a36Sopenharmony_ci		return ret;
39862306a36Sopenharmony_ci	if (datum > 1)
39962306a36Sopenharmony_ci		return -EINVAL;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
40262306a36Sopenharmony_ci	*((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
40362306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(uint32_t));
40462306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
40562306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_BOOTFLAG);
40662306a36Sopenharmony_ci	if (ret)
40762306a36Sopenharmony_ci		return ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return strlen(buf);
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/**
41362306a36Sopenharmony_ci * boottime_show - Get function for sysfs attribute boottime
41462306a36Sopenharmony_ci * @dev: the &struct device
41562306a36Sopenharmony_ci * @attr: device attributes
41662306a36Sopenharmony_ci * @buf: buffer where data will be returned
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_cistatic ssize_t boottime_show(struct device *dev,
41962306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
42262306a36Sopenharmony_ci	int ret;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (ret)
42762306a36Sopenharmony_ci		return ret;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", defs.boottime);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/**
43362306a36Sopenharmony_ci * boottime_store - Set function for sysfs attribute boottime
43462306a36Sopenharmony_ci * @dev: the &struct device
43562306a36Sopenharmony_ci * @attr: device attributes
43662306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
43762306a36Sopenharmony_ci * @count: size of buffer
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_cistatic ssize_t boottime_store(struct device *dev,
44062306a36Sopenharmony_ci			      struct device_attribute *attr,
44162306a36Sopenharmony_ci			      const char *buf, size_t count)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
44462306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
44562306a36Sopenharmony_ci	uint32_t datum;
44662306a36Sopenharmony_ci	int ret;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, &datum);
44962306a36Sopenharmony_ci	if (ret)
45062306a36Sopenharmony_ci		return ret;
45162306a36Sopenharmony_ci	if (datum > 255)
45262306a36Sopenharmony_ci		return -EINVAL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* A too small boot time will result in the device booting into
45762306a36Sopenharmony_ci	 * standalone (no-host) mode before the host can take control of it,
45862306a36Sopenharmony_ci	 * so the change will be hard to revert.  This may be a desired
45962306a36Sopenharmony_ci	 * feature (e.g to configure a very fast boot time for devices that
46062306a36Sopenharmony_ci	 * will not be attached to a host), but dangerous.  So I'm enforcing a
46162306a36Sopenharmony_ci	 * lower limit of 20 seconds:  remove and recompile the driver if this
46262306a36Sopenharmony_ci	 * does not work for you.
46362306a36Sopenharmony_ci	 */
46462306a36Sopenharmony_ci	datum = (datum < 20) ? 20 : datum;
46562306a36Sopenharmony_ci	cmd.data[0] = datum;
46662306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(uint8_t));
46762306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
46862306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_BOOTTIME);
46962306a36Sopenharmony_ci	if (ret)
47062306a36Sopenharmony_ci		return ret;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return strlen(buf);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci/**
47662306a36Sopenharmony_ci * channel_show - Get function for sysfs attribute channel
47762306a36Sopenharmony_ci * @dev: the &struct device
47862306a36Sopenharmony_ci * @attr: device attributes
47962306a36Sopenharmony_ci * @buf: buffer where data will be returned
48062306a36Sopenharmony_ci */
48162306a36Sopenharmony_cistatic ssize_t channel_show(struct device *dev,
48262306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
48562306a36Sopenharmony_ci	int ret;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (ret)
49062306a36Sopenharmony_ci		return ret;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", le16_to_cpu(defs.channel));
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/**
49662306a36Sopenharmony_ci * channel_store - Set function for sysfs attribute channel
49762306a36Sopenharmony_ci * @dev: the &struct device
49862306a36Sopenharmony_ci * @attr: device attributes
49962306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
50062306a36Sopenharmony_ci * @count: size of buffer
50162306a36Sopenharmony_ci */
50262306a36Sopenharmony_cistatic ssize_t channel_store(struct device *dev, struct device_attribute *attr,
50362306a36Sopenharmony_ci			     const char *buf, size_t count)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
50662306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
50762306a36Sopenharmony_ci	uint32_t datum;
50862306a36Sopenharmony_ci	int ret;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, &datum);
51162306a36Sopenharmony_ci	if (ret)
51262306a36Sopenharmony_ci		return ret;
51362306a36Sopenharmony_ci	if (datum < 1 || datum > 11)
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
51762306a36Sopenharmony_ci	*((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
51862306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(uint16_t));
51962306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
52062306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_DEF_CHANNEL);
52162306a36Sopenharmony_ci	if (ret)
52262306a36Sopenharmony_ci		return ret;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return strlen(buf);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/**
52862306a36Sopenharmony_ci * mesh_id_show - Get function for sysfs attribute mesh_id
52962306a36Sopenharmony_ci * @dev: the &struct device
53062306a36Sopenharmony_ci * @attr: device attributes
53162306a36Sopenharmony_ci * @buf: buffer where data will be returned
53262306a36Sopenharmony_ci */
53362306a36Sopenharmony_cistatic ssize_t mesh_id_show(struct device *dev, struct device_attribute *attr,
53462306a36Sopenharmony_ci			    char *buf)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
53762306a36Sopenharmony_ci	int ret;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (ret)
54262306a36Sopenharmony_ci		return ret;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
54562306a36Sopenharmony_ci		dev_err(dev, "inconsistent mesh ID length\n");
54662306a36Sopenharmony_ci		defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);
55062306a36Sopenharmony_ci	buf[defs.meshie.val.mesh_id_len] = '\n';
55162306a36Sopenharmony_ci	buf[defs.meshie.val.mesh_id_len + 1] = '\0';
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return defs.meshie.val.mesh_id_len + 1;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/**
55762306a36Sopenharmony_ci * mesh_id_store - Set function for sysfs attribute mesh_id
55862306a36Sopenharmony_ci * @dev: the &struct device
55962306a36Sopenharmony_ci * @attr: device attributes
56062306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
56162306a36Sopenharmony_ci * @count: size of buffer
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_cistatic ssize_t mesh_id_store(struct device *dev, struct device_attribute *attr,
56462306a36Sopenharmony_ci			     const char *buf, size_t count)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
56762306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
56862306a36Sopenharmony_ci	struct mrvl_meshie *ie;
56962306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
57062306a36Sopenharmony_ci	int len;
57162306a36Sopenharmony_ci	int ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
57462306a36Sopenharmony_ci		return -EINVAL;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
57762306a36Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* fetch all other Information Element parameters */
58062306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* transfer IE elements */
58562306a36Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	len = count - 1;
58862306a36Sopenharmony_ci	memcpy(ie->val.mesh_id, buf, len);
58962306a36Sopenharmony_ci	/* SSID len */
59062306a36Sopenharmony_ci	ie->val.mesh_id_len = len;
59162306a36Sopenharmony_ci	/* IE len */
59262306a36Sopenharmony_ci	ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
59562306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
59662306a36Sopenharmony_ci	if (ret)
59762306a36Sopenharmony_ci		return ret;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	return strlen(buf);
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci/**
60362306a36Sopenharmony_ci * protocol_id_show - Get function for sysfs attribute protocol_id
60462306a36Sopenharmony_ci * @dev: the &struct device
60562306a36Sopenharmony_ci * @attr: device attributes
60662306a36Sopenharmony_ci * @buf: buffer where data will be returned
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_cistatic ssize_t protocol_id_show(struct device *dev,
60962306a36Sopenharmony_ci				struct device_attribute *attr,
61062306a36Sopenharmony_ci				char *buf)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
61362306a36Sopenharmony_ci	int ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (ret)
61862306a36Sopenharmony_ci		return ret;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", defs.meshie.val.active_protocol_id);
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/**
62462306a36Sopenharmony_ci * protocol_id_store - Set function for sysfs attribute protocol_id
62562306a36Sopenharmony_ci * @dev: the &struct device
62662306a36Sopenharmony_ci * @attr: device attributes
62762306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
62862306a36Sopenharmony_ci * @count: size of buffer
62962306a36Sopenharmony_ci */
63062306a36Sopenharmony_cistatic ssize_t protocol_id_store(struct device *dev,
63162306a36Sopenharmony_ci				 struct device_attribute *attr,
63262306a36Sopenharmony_ci				 const char *buf, size_t count)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
63562306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
63662306a36Sopenharmony_ci	struct mrvl_meshie *ie;
63762306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
63862306a36Sopenharmony_ci	uint32_t datum;
63962306a36Sopenharmony_ci	int ret;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ret = kstrtouint(buf, 10, &datum);
64262306a36Sopenharmony_ci	if (ret)
64362306a36Sopenharmony_ci		return ret;
64462306a36Sopenharmony_ci	if (datum > 255)
64562306a36Sopenharmony_ci		return -EINVAL;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* fetch all other Information Element parameters */
65062306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* transfer IE elements */
65562306a36Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
65662306a36Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
65762306a36Sopenharmony_ci	/* update protocol id */
65862306a36Sopenharmony_ci	ie->val.active_protocol_id = datum;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
66162306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
66262306a36Sopenharmony_ci	if (ret)
66362306a36Sopenharmony_ci		return ret;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return strlen(buf);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci/**
66962306a36Sopenharmony_ci * metric_id_show - Get function for sysfs attribute metric_id
67062306a36Sopenharmony_ci * @dev: the &struct device
67162306a36Sopenharmony_ci * @attr: device attributes
67262306a36Sopenharmony_ci * @buf: buffer where data will be returned
67362306a36Sopenharmony_ci */
67462306a36Sopenharmony_cistatic ssize_t metric_id_show(struct device *dev,
67562306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
67862306a36Sopenharmony_ci	int ret;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (ret)
68362306a36Sopenharmony_ci		return ret;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", defs.meshie.val.active_metric_id);
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci/**
68962306a36Sopenharmony_ci * metric_id_store - Set function for sysfs attribute metric_id
69062306a36Sopenharmony_ci * @dev: the &struct device
69162306a36Sopenharmony_ci * @attr: device attributes
69262306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
69362306a36Sopenharmony_ci * @count: size of buffer
69462306a36Sopenharmony_ci */
69562306a36Sopenharmony_cistatic ssize_t metric_id_store(struct device *dev,
69662306a36Sopenharmony_ci			       struct device_attribute *attr,
69762306a36Sopenharmony_ci			       const char *buf, size_t count)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
70062306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
70162306a36Sopenharmony_ci	struct mrvl_meshie *ie;
70262306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
70362306a36Sopenharmony_ci	uint32_t datum;
70462306a36Sopenharmony_ci	int ret;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
70762306a36Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
70862306a36Sopenharmony_ci	if ((ret != 1) || (datum > 255))
70962306a36Sopenharmony_ci		return -EINVAL;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* fetch all other Information Element parameters */
71262306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* transfer IE elements */
71762306a36Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
71862306a36Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
71962306a36Sopenharmony_ci	/* update metric id */
72062306a36Sopenharmony_ci	ie->val.active_metric_id = datum;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
72362306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
72462306a36Sopenharmony_ci	if (ret)
72562306a36Sopenharmony_ci		return ret;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return strlen(buf);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/**
73162306a36Sopenharmony_ci * capability_show - Get function for sysfs attribute capability
73262306a36Sopenharmony_ci * @dev: the &struct device
73362306a36Sopenharmony_ci * @attr: device attributes
73462306a36Sopenharmony_ci * @buf: buffer where data will be returned
73562306a36Sopenharmony_ci */
73662306a36Sopenharmony_cistatic ssize_t capability_show(struct device *dev,
73762306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
74062306a36Sopenharmony_ci	int ret;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (ret)
74562306a36Sopenharmony_ci		return ret;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", defs.meshie.val.mesh_capability);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci/**
75162306a36Sopenharmony_ci * capability_store - Set function for sysfs attribute capability
75262306a36Sopenharmony_ci * @dev: the &struct device
75362306a36Sopenharmony_ci * @attr: device attributes
75462306a36Sopenharmony_ci * @buf: buffer that contains new attribute value
75562306a36Sopenharmony_ci * @count: size of buffer
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_cistatic ssize_t capability_store(struct device *dev,
75862306a36Sopenharmony_ci				struct device_attribute *attr,
75962306a36Sopenharmony_ci				const char *buf, size_t count)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
76262306a36Sopenharmony_ci	struct mrvl_mesh_defaults defs;
76362306a36Sopenharmony_ci	struct mrvl_meshie *ie;
76462306a36Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
76562306a36Sopenharmony_ci	uint32_t datum;
76662306a36Sopenharmony_ci	int ret;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
76962306a36Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
77062306a36Sopenharmony_ci	if ((ret != 1) || (datum > 255))
77162306a36Sopenharmony_ci		return -EINVAL;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	/* fetch all other Information Element parameters */
77462306a36Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* transfer IE elements */
77962306a36Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
78062306a36Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
78162306a36Sopenharmony_ci	/* update value */
78262306a36Sopenharmony_ci	ie->val.mesh_capability = datum;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
78562306a36Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
78662306a36Sopenharmony_ci	if (ret)
78762306a36Sopenharmony_ci		return ret;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	return strlen(buf);
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(bootflag);
79462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(boottime);
79562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(channel);
79662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(mesh_id);
79762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(protocol_id);
79862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(metric_id);
79962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(capability);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic struct attribute *boot_opts_attrs[] = {
80262306a36Sopenharmony_ci	&dev_attr_bootflag.attr,
80362306a36Sopenharmony_ci	&dev_attr_boottime.attr,
80462306a36Sopenharmony_ci	&dev_attr_channel.attr,
80562306a36Sopenharmony_ci	NULL
80662306a36Sopenharmony_ci};
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic const struct attribute_group boot_opts_group = {
80962306a36Sopenharmony_ci	.name = "boot_options",
81062306a36Sopenharmony_ci	.attrs = boot_opts_attrs,
81162306a36Sopenharmony_ci};
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic struct attribute *mesh_ie_attrs[] = {
81462306a36Sopenharmony_ci	&dev_attr_mesh_id.attr,
81562306a36Sopenharmony_ci	&dev_attr_protocol_id.attr,
81662306a36Sopenharmony_ci	&dev_attr_metric_id.attr,
81762306a36Sopenharmony_ci	&dev_attr_capability.attr,
81862306a36Sopenharmony_ci	NULL
81962306a36Sopenharmony_ci};
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic const struct attribute_group mesh_ie_group = {
82262306a36Sopenharmony_ci	.name = "mesh_ie",
82362306a36Sopenharmony_ci	.attrs = mesh_ie_attrs,
82462306a36Sopenharmony_ci};
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci/***************************************************************************
82862306a36Sopenharmony_ci * Initializing and starting, stopping mesh
82962306a36Sopenharmony_ci */
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/*
83262306a36Sopenharmony_ci * Check mesh FW version and appropriately send the mesh start
83362306a36Sopenharmony_ci * command
83462306a36Sopenharmony_ci */
83562306a36Sopenharmony_civoid lbs_init_mesh(struct lbs_private *priv)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	/* Determine mesh_fw_ver from fwrelease and fwcapinfo */
83862306a36Sopenharmony_ci	/* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
83962306a36Sopenharmony_ci	/* 5.110.22 have mesh command with 0xa3 command id */
84062306a36Sopenharmony_ci	/* 10.0.0.p0 FW brings in mesh config command with different id */
84162306a36Sopenharmony_ci	/* Check FW version MSB and initialize mesh_fw_ver */
84262306a36Sopenharmony_ci	if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
84362306a36Sopenharmony_ci		/* Enable mesh, if supported, and work out which TLV it uses.
84462306a36Sopenharmony_ci		   0x100 + 291 is an unofficial value used in 5.110.20.pXX
84562306a36Sopenharmony_ci		   0x100 + 37 is the official value used in 5.110.21.pXX
84662306a36Sopenharmony_ci		   but we check them in that order because 20.pXX doesn't
84762306a36Sopenharmony_ci		   give an error -- it just silently fails. */
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		/* 5.110.20.pXX firmware will fail the command if the channel
85062306a36Sopenharmony_ci		   doesn't match the existing channel. But only if the TLV
85162306a36Sopenharmony_ci		   is correct. If the channel is wrong, _BOTH_ versions will
85262306a36Sopenharmony_ci		   give an error to 0x100+291, and allow 0x100+37 to succeed.
85362306a36Sopenharmony_ci		   It's just that 5.110.20.pXX will not have done anything
85462306a36Sopenharmony_ci		   useful */
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
85762306a36Sopenharmony_ci		if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
85862306a36Sopenharmony_ci			priv->mesh_tlv = TLV_TYPE_MESH_ID;
85962306a36Sopenharmony_ci			if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
86062306a36Sopenharmony_ci				priv->mesh_tlv = 0;
86162306a36Sopenharmony_ci		}
86262306a36Sopenharmony_ci	} else
86362306a36Sopenharmony_ci	if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
86462306a36Sopenharmony_ci		(priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
86562306a36Sopenharmony_ci		/* 10.0.0.pXX new firmwares should succeed with TLV
86662306a36Sopenharmony_ci		 * 0x100+37; Do not invoke command with old TLV.
86762306a36Sopenharmony_ci		 */
86862306a36Sopenharmony_ci		priv->mesh_tlv = TLV_TYPE_MESH_ID;
86962306a36Sopenharmony_ci		if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
87062306a36Sopenharmony_ci			priv->mesh_tlv = 0;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* Stop meshing until interface is brought up */
87462306a36Sopenharmony_ci	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_civoid lbs_start_mesh(struct lbs_private *priv)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	lbs_add_mesh(priv);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
88262306a36Sopenharmony_ci		netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ciint lbs_deinit_mesh(struct lbs_private *priv)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct net_device *dev = priv->dev;
88862306a36Sopenharmony_ci	int ret = 0;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (priv->mesh_tlv) {
89162306a36Sopenharmony_ci		device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
89262306a36Sopenharmony_ci		ret = 1;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	return ret;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/**
90062306a36Sopenharmony_ci * lbs_mesh_stop - close the mshX interface
90162306a36Sopenharmony_ci *
90262306a36Sopenharmony_ci * @dev:	A pointer to &net_device structure
90362306a36Sopenharmony_ci * returns:	0
90462306a36Sopenharmony_ci */
90562306a36Sopenharmony_cistatic int lbs_mesh_stop(struct net_device *dev)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
91062306a36Sopenharmony_ci		lbs_mesh_get_channel(priv));
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	spin_lock_irq(&priv->driver_lock);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	netif_stop_queue(dev);
91562306a36Sopenharmony_ci	netif_carrier_off(dev);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	spin_unlock_irq(&priv->driver_lock);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	lbs_update_mcast(priv);
92062306a36Sopenharmony_ci	if (!lbs_iface_active(priv))
92162306a36Sopenharmony_ci		lbs_stop_iface(priv);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return 0;
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/**
92762306a36Sopenharmony_ci * lbs_mesh_dev_open - open the mshX interface
92862306a36Sopenharmony_ci *
92962306a36Sopenharmony_ci * @dev:	A pointer to &net_device structure
93062306a36Sopenharmony_ci * returns:	0 or -EBUSY if monitor mode active
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_cistatic int lbs_mesh_dev_open(struct net_device *dev)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
93562306a36Sopenharmony_ci	int ret = 0;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (!priv->iface_running) {
93862306a36Sopenharmony_ci		ret = lbs_start_iface(priv);
93962306a36Sopenharmony_ci		if (ret)
94062306a36Sopenharmony_ci			goto out;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	spin_lock_irq(&priv->driver_lock);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
94662306a36Sopenharmony_ci		ret = -EBUSY;
94762306a36Sopenharmony_ci		spin_unlock_irq(&priv->driver_lock);
94862306a36Sopenharmony_ci		goto out;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	netif_carrier_on(dev);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (!priv->tx_pending_len)
95462306a36Sopenharmony_ci		netif_wake_queue(dev);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	spin_unlock_irq(&priv->driver_lock);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
95962306a36Sopenharmony_ci		lbs_mesh_get_channel(priv));
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ciout:
96262306a36Sopenharmony_ci	return ret;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic const struct net_device_ops mesh_netdev_ops = {
96662306a36Sopenharmony_ci	.ndo_open		= lbs_mesh_dev_open,
96762306a36Sopenharmony_ci	.ndo_stop 		= lbs_mesh_stop,
96862306a36Sopenharmony_ci	.ndo_start_xmit		= lbs_hard_start_xmit,
96962306a36Sopenharmony_ci	.ndo_set_mac_address	= lbs_set_mac_address,
97062306a36Sopenharmony_ci	.ndo_set_rx_mode	= lbs_set_multicast_list,
97162306a36Sopenharmony_ci};
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci/**
97462306a36Sopenharmony_ci * lbs_add_mesh - add mshX interface
97562306a36Sopenharmony_ci *
97662306a36Sopenharmony_ci * @priv:	A pointer to the &struct lbs_private structure
97762306a36Sopenharmony_ci * returns:	0 if successful, -X otherwise
97862306a36Sopenharmony_ci */
97962306a36Sopenharmony_cistatic int lbs_add_mesh(struct lbs_private *priv)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct net_device *mesh_dev = NULL;
98262306a36Sopenharmony_ci	struct wireless_dev *mesh_wdev;
98362306a36Sopenharmony_ci	int ret = 0;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Allocate a virtual mesh device */
98662306a36Sopenharmony_ci	mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
98762306a36Sopenharmony_ci	if (!mesh_wdev) {
98862306a36Sopenharmony_ci		lbs_deb_mesh("init mshX wireless device failed\n");
98962306a36Sopenharmony_ci		ret = -ENOMEM;
99062306a36Sopenharmony_ci		goto done;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup);
99462306a36Sopenharmony_ci	if (!mesh_dev) {
99562306a36Sopenharmony_ci		lbs_deb_mesh("init mshX device failed\n");
99662306a36Sopenharmony_ci		ret = -ENOMEM;
99762306a36Sopenharmony_ci		goto err_free_wdev;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
100162306a36Sopenharmony_ci	mesh_wdev->wiphy = priv->wdev->wiphy;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	if (priv->mesh_tlv) {
100462306a36Sopenharmony_ci		sprintf(mesh_wdev->u.mesh.id, "mesh");
100562306a36Sopenharmony_ci		mesh_wdev->u.mesh.id_up_len = 4;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	mesh_wdev->netdev = mesh_dev;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	mesh_dev->ml_priv = priv;
101162306a36Sopenharmony_ci	mesh_dev->ieee80211_ptr = mesh_wdev;
101262306a36Sopenharmony_ci	priv->mesh_dev = mesh_dev;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	mesh_dev->netdev_ops = &mesh_netdev_ops;
101562306a36Sopenharmony_ci	mesh_dev->ethtool_ops = &lbs_ethtool_ops;
101662306a36Sopenharmony_ci	eth_hw_addr_inherit(mesh_dev, priv->dev);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
102162306a36Sopenharmony_ci	mesh_dev->sysfs_groups[0] = &lbs_mesh_attr_group;
102262306a36Sopenharmony_ci	mesh_dev->sysfs_groups[1] = &boot_opts_group;
102362306a36Sopenharmony_ci	mesh_dev->sysfs_groups[2] = &mesh_ie_group;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	/* Register virtual mesh interface */
102662306a36Sopenharmony_ci	ret = register_netdev(mesh_dev);
102762306a36Sopenharmony_ci	if (ret) {
102862306a36Sopenharmony_ci		pr_err("cannot register mshX virtual interface\n");
102962306a36Sopenharmony_ci		goto err_free_netdev;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Everything successful */
103362306a36Sopenharmony_ci	ret = 0;
103462306a36Sopenharmony_ci	goto done;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cierr_free_netdev:
103762306a36Sopenharmony_ci	free_netdev(mesh_dev);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cierr_free_wdev:
104062306a36Sopenharmony_ci	kfree(mesh_wdev);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cidone:
104362306a36Sopenharmony_ci	return ret;
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_civoid lbs_remove_mesh(struct lbs_private *priv)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	struct net_device *mesh_dev;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	mesh_dev = priv->mesh_dev;
105162306a36Sopenharmony_ci	if (!mesh_dev)
105262306a36Sopenharmony_ci		return;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	netif_stop_queue(mesh_dev);
105562306a36Sopenharmony_ci	netif_carrier_off(mesh_dev);
105662306a36Sopenharmony_ci	unregister_netdev(mesh_dev);
105762306a36Sopenharmony_ci	priv->mesh_dev = NULL;
105862306a36Sopenharmony_ci	kfree(mesh_dev->ieee80211_ptr);
105962306a36Sopenharmony_ci	free_netdev(mesh_dev);
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci/***************************************************************************
106462306a36Sopenharmony_ci * Sending and receiving
106562306a36Sopenharmony_ci */
106662306a36Sopenharmony_cistruct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
106762306a36Sopenharmony_ci	struct net_device *dev, struct rxpd *rxpd)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	if (priv->mesh_dev) {
107062306a36Sopenharmony_ci		if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
107162306a36Sopenharmony_ci			if (rxpd->rx_control & RxPD_MESH_FRAME)
107262306a36Sopenharmony_ci				dev = priv->mesh_dev;
107362306a36Sopenharmony_ci		} else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
107462306a36Sopenharmony_ci			if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
107562306a36Sopenharmony_ci				dev = priv->mesh_dev;
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci	return dev;
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_civoid lbs_mesh_set_txpd(struct lbs_private *priv,
108362306a36Sopenharmony_ci	struct net_device *dev, struct txpd *txpd)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	if (dev == priv->mesh_dev) {
108662306a36Sopenharmony_ci		if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
108762306a36Sopenharmony_ci			txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
108862306a36Sopenharmony_ci		else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
108962306a36Sopenharmony_ci			txpd->u.bss.bss_num = MESH_IFACE_ID;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci/***************************************************************************
109562306a36Sopenharmony_ci * Ethtool related
109662306a36Sopenharmony_ci */
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic const char mesh_stat_strings[MESH_STATS_NUM][ETH_GSTRING_LEN] = {
109962306a36Sopenharmony_ci	"drop_duplicate_bcast",
110062306a36Sopenharmony_ci	"drop_ttl_zero",
110162306a36Sopenharmony_ci	"drop_no_fwd_route",
110262306a36Sopenharmony_ci	"drop_no_buffers",
110362306a36Sopenharmony_ci	"fwded_unicast_cnt",
110462306a36Sopenharmony_ci	"fwded_bcast_cnt",
110562306a36Sopenharmony_ci	"drop_blind_table",
110662306a36Sopenharmony_ci	"tx_failed_cnt"
110762306a36Sopenharmony_ci};
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_civoid lbs_mesh_ethtool_get_stats(struct net_device *dev,
111062306a36Sopenharmony_ci	struct ethtool_stats *stats, uint64_t *data)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
111362306a36Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
111462306a36Sopenharmony_ci	int ret;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	/* Get Mesh Statistics */
111762306a36Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (ret) {
112062306a36Sopenharmony_ci		memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
112162306a36Sopenharmony_ci		return;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
112562306a36Sopenharmony_ci	priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
112662306a36Sopenharmony_ci	priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
112762306a36Sopenharmony_ci	priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
112862306a36Sopenharmony_ci	priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
112962306a36Sopenharmony_ci	priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
113062306a36Sopenharmony_ci	priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
113162306a36Sopenharmony_ci	priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	data[0] = priv->mstats.fwd_drop_rbt;
113462306a36Sopenharmony_ci	data[1] = priv->mstats.fwd_drop_ttl;
113562306a36Sopenharmony_ci	data[2] = priv->mstats.fwd_drop_noroute;
113662306a36Sopenharmony_ci	data[3] = priv->mstats.fwd_drop_nobuf;
113762306a36Sopenharmony_ci	data[4] = priv->mstats.fwd_unicast_cnt;
113862306a36Sopenharmony_ci	data[5] = priv->mstats.fwd_bcast_cnt;
113962306a36Sopenharmony_ci	data[6] = priv->mstats.drop_blind;
114062306a36Sopenharmony_ci	data[7] = priv->mstats.tx_failed_cnt;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ciint lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
114862306a36Sopenharmony_ci		return MESH_STATS_NUM;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	return -EOPNOTSUPP;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_civoid lbs_mesh_ethtool_get_strings(struct net_device *dev,
115462306a36Sopenharmony_ci	uint32_t stringset, uint8_t *s)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	switch (stringset) {
115762306a36Sopenharmony_ci	case ETH_SS_STATS:
115862306a36Sopenharmony_ci		memcpy(s, mesh_stat_strings, sizeof(mesh_stat_strings));
115962306a36Sopenharmony_ci		break;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci}
1162