18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/delay.h>
58c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
68c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
78c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
88c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
98c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
108c2ecf20Sopenharmony_ci#include <linux/kthread.h>
118c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
128c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "mesh.h"
158c2ecf20Sopenharmony_ci#include "decl.h"
168c2ecf20Sopenharmony_ci#include "cmd.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int lbs_add_mesh(struct lbs_private *priv);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/***************************************************************************
228c2ecf20Sopenharmony_ci * Mesh command handling
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
268c2ecf20Sopenharmony_ci		    struct cmd_ds_mesh_access *cmd)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	int ret;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
318c2ecf20Sopenharmony_ci	cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
328c2ecf20Sopenharmony_ci	cmd->hdr.result = 0;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(cmd_action);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return ret;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int __lbs_mesh_config_send(struct lbs_private *priv,
428c2ecf20Sopenharmony_ci				  struct cmd_ds_mesh_config *cmd,
438c2ecf20Sopenharmony_ci				  uint16_t action, uint16_t type)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	int ret;
468c2ecf20Sopenharmony_ci	u16 command = CMD_MESH_CONFIG_OLD;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * Command id is 0xac for v10 FW along with mesh interface
508c2ecf20Sopenharmony_ci	 * id in bits 14-13-12.
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
538c2ecf20Sopenharmony_ci		command = CMD_MESH_CONFIG |
548c2ecf20Sopenharmony_ci			  (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	cmd->hdr.command = cpu_to_le16(command);
578c2ecf20Sopenharmony_ci	cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
588c2ecf20Sopenharmony_ci	cmd->hdr.result = 0;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	cmd->type = cpu_to_le16(type);
618c2ecf20Sopenharmony_ci	cmd->action = cpu_to_le16(action);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ret = lbs_cmd_with_response(priv, command, cmd);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return ret;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int lbs_mesh_config_send(struct lbs_private *priv,
698c2ecf20Sopenharmony_ci			 struct cmd_ds_mesh_config *cmd,
708c2ecf20Sopenharmony_ci			 uint16_t action, uint16_t type)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	int ret;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
758c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	ret = __lbs_mesh_config_send(priv, cmd, action, type);
788c2ecf20Sopenharmony_ci	return ret;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* This function is the CMD_MESH_CONFIG legacy function.  It only handles the
828c2ecf20Sopenharmony_ci * START and STOP actions.  The extended actions supported by CMD_MESH_CONFIG
838c2ecf20Sopenharmony_ci * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
848c2ecf20Sopenharmony_ci * lbs_mesh_config_send.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
878c2ecf20Sopenharmony_ci		uint16_t chan)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct wireless_dev *mesh_wdev;
908c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
918c2ecf20Sopenharmony_ci	struct mrvl_meshie *ie;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
948c2ecf20Sopenharmony_ci	cmd.channel = cpu_to_le16(chan);
958c2ecf20Sopenharmony_ci	ie = (struct mrvl_meshie *)cmd.data;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	switch (action) {
988c2ecf20Sopenharmony_ci	case CMD_ACT_MESH_CONFIG_START:
998c2ecf20Sopenharmony_ci		ie->id = WLAN_EID_VENDOR_SPECIFIC;
1008c2ecf20Sopenharmony_ci		ie->val.oui[0] = 0x00;
1018c2ecf20Sopenharmony_ci		ie->val.oui[1] = 0x50;
1028c2ecf20Sopenharmony_ci		ie->val.oui[2] = 0x43;
1038c2ecf20Sopenharmony_ci		ie->val.type = MARVELL_MESH_IE_TYPE;
1048c2ecf20Sopenharmony_ci		ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
1058c2ecf20Sopenharmony_ci		ie->val.version = MARVELL_MESH_IE_VERSION;
1068c2ecf20Sopenharmony_ci		ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
1078c2ecf20Sopenharmony_ci		ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
1088c2ecf20Sopenharmony_ci		ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		if (priv->mesh_dev) {
1118c2ecf20Sopenharmony_ci			mesh_wdev = priv->mesh_dev->ieee80211_ptr;
1128c2ecf20Sopenharmony_ci			ie->val.mesh_id_len = mesh_wdev->mesh_id_up_len;
1138c2ecf20Sopenharmony_ci			memcpy(ie->val.mesh_id, mesh_wdev->ssid,
1148c2ecf20Sopenharmony_ci						mesh_wdev->mesh_id_up_len);
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		ie->len = sizeof(struct mrvl_meshie_val) -
1188c2ecf20Sopenharmony_ci			IEEE80211_MAX_SSID_LEN + ie->val.mesh_id_len;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case CMD_ACT_MESH_CONFIG_STOP:
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	default:
1258c2ecf20Sopenharmony_ci		return -1;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n",
1288c2ecf20Sopenharmony_ci		    action, priv->mesh_tlv, chan, ie->val.mesh_id_len,
1298c2ecf20Sopenharmony_ci		    ie->val.mesh_id);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ciint lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	priv->mesh_channel = channel;
1378c2ecf20Sopenharmony_ci	return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	return priv->mesh_channel ?: 1;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/***************************************************************************
1468c2ecf20Sopenharmony_ci * Mesh sysfs support
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * Attributes exported through sysfs
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/**
1548c2ecf20Sopenharmony_ci * lbs_anycast_get - Get function for sysfs attribute anycast_mask
1558c2ecf20Sopenharmony_ci * @dev: the &struct device
1568c2ecf20Sopenharmony_ci * @attr: device attributes
1578c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_cistatic ssize_t lbs_anycast_get(struct device *dev,
1608c2ecf20Sopenharmony_ci		struct device_attribute *attr, char * buf)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
1638c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
1648c2ecf20Sopenharmony_ci	int ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
1698c2ecf20Sopenharmony_ci	if (ret)
1708c2ecf20Sopenharmony_ci		return ret;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/**
1768c2ecf20Sopenharmony_ci * lbs_anycast_set - Set function for sysfs attribute anycast_mask
1778c2ecf20Sopenharmony_ci * @dev: the &struct device
1788c2ecf20Sopenharmony_ci * @attr: device attributes
1798c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
1808c2ecf20Sopenharmony_ci * @count: size of buffer
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_cistatic ssize_t lbs_anycast_set(struct device *dev,
1838c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char * buf, size_t count)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
1868c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
1878c2ecf20Sopenharmony_ci	uint32_t datum;
1888c2ecf20Sopenharmony_ci	int ret;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
1918c2ecf20Sopenharmony_ci	sscanf(buf, "%x", &datum);
1928c2ecf20Sopenharmony_ci	mesh_access.data[0] = cpu_to_le32(datum);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
1958c2ecf20Sopenharmony_ci	if (ret)
1968c2ecf20Sopenharmony_ci		return ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return strlen(buf);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/**
2028c2ecf20Sopenharmony_ci * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit
2038c2ecf20Sopenharmony_ci * @dev: the &struct device
2048c2ecf20Sopenharmony_ci * @attr: device attributes
2058c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_cistatic ssize_t lbs_prb_rsp_limit_get(struct device *dev,
2088c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
2118c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
2128c2ecf20Sopenharmony_ci	int ret;
2138c2ecf20Sopenharmony_ci	u32 retry_limit;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
2168c2ecf20Sopenharmony_ci	mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
2198c2ecf20Sopenharmony_ci			&mesh_access);
2208c2ecf20Sopenharmony_ci	if (ret)
2218c2ecf20Sopenharmony_ci		return ret;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	retry_limit = le32_to_cpu(mesh_access.data[1]);
2248c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "%d\n", retry_limit);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/**
2288c2ecf20Sopenharmony_ci * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit
2298c2ecf20Sopenharmony_ci * @dev: the &struct device
2308c2ecf20Sopenharmony_ci * @attr: device attributes
2318c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
2328c2ecf20Sopenharmony_ci * @count: size of buffer
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic ssize_t lbs_prb_rsp_limit_set(struct device *dev,
2358c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
2388c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
2398c2ecf20Sopenharmony_ci	int ret;
2408c2ecf20Sopenharmony_ci	unsigned long retry_limit;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	memset(&mesh_access, 0, sizeof(mesh_access));
2438c2ecf20Sopenharmony_ci	mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 10, &retry_limit);
2468c2ecf20Sopenharmony_ci	if (ret)
2478c2ecf20Sopenharmony_ci		return ret;
2488c2ecf20Sopenharmony_ci	if (retry_limit > 15)
2498c2ecf20Sopenharmony_ci		return -ENOTSUPP;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	mesh_access.data[1] = cpu_to_le32(retry_limit);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
2548c2ecf20Sopenharmony_ci			&mesh_access);
2558c2ecf20Sopenharmony_ci	if (ret)
2568c2ecf20Sopenharmony_ci		return ret;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return strlen(buf);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/**
2628c2ecf20Sopenharmony_ci * lbs_mesh_get - Get function for sysfs attribute mesh
2638c2ecf20Sopenharmony_ci * @dev: the &struct device
2648c2ecf20Sopenharmony_ci * @attr: device attributes
2658c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
2668c2ecf20Sopenharmony_ci */
2678c2ecf20Sopenharmony_cistatic ssize_t lbs_mesh_get(struct device *dev,
2688c2ecf20Sopenharmony_ci		struct device_attribute *attr, char * buf)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
2718c2ecf20Sopenharmony_ci	return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/**
2758c2ecf20Sopenharmony_ci * lbs_mesh_set - Set function for sysfs attribute mesh
2768c2ecf20Sopenharmony_ci * @dev: the &struct device
2778c2ecf20Sopenharmony_ci * @attr: device attributes
2788c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
2798c2ecf20Sopenharmony_ci * @count: size of buffer
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_cistatic ssize_t lbs_mesh_set(struct device *dev,
2828c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char * buf, size_t count)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
2858c2ecf20Sopenharmony_ci	int enable;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	sscanf(buf, "%x", &enable);
2888c2ecf20Sopenharmony_ci	enable = !!enable;
2898c2ecf20Sopenharmony_ci	if (enable == !!priv->mesh_dev)
2908c2ecf20Sopenharmony_ci		return count;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (enable)
2938c2ecf20Sopenharmony_ci		lbs_add_mesh(priv);
2948c2ecf20Sopenharmony_ci	else
2958c2ecf20Sopenharmony_ci		lbs_remove_mesh(priv);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return count;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/*
3018c2ecf20Sopenharmony_ci * lbs_mesh attribute to be exported per ethX interface
3028c2ecf20Sopenharmony_ci * through sysfs (/sys/class/net/ethX/lbs_mesh)
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/*
3078c2ecf20Sopenharmony_ci * anycast_mask attribute to be exported per mshX interface
3088c2ecf20Sopenharmony_ci * through sysfs (/sys/class/net/mshX/anycast_mask)
3098c2ecf20Sopenharmony_ci */
3108c2ecf20Sopenharmony_cistatic DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/*
3138c2ecf20Sopenharmony_ci * prb_rsp_limit attribute to be exported per mshX interface
3148c2ecf20Sopenharmony_ci * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
3178c2ecf20Sopenharmony_ci		lbs_prb_rsp_limit_set);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic struct attribute *lbs_mesh_sysfs_entries[] = {
3208c2ecf20Sopenharmony_ci	&dev_attr_anycast_mask.attr,
3218c2ecf20Sopenharmony_ci	&dev_attr_prb_rsp_limit.attr,
3228c2ecf20Sopenharmony_ci	NULL,
3238c2ecf20Sopenharmony_ci};
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic const struct attribute_group lbs_mesh_attr_group = {
3268c2ecf20Sopenharmony_ci	.attrs = lbs_mesh_sysfs_entries,
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/***************************************************************************
3318c2ecf20Sopenharmony_ci * Persistent configuration support
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int mesh_get_default_parameters(struct device *dev,
3358c2ecf20Sopenharmony_ci				       struct mrvl_mesh_defaults *defs)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
3388c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
3398c2ecf20Sopenharmony_ci	int ret;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
3428c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
3438c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_GET_DEFAULTS);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (ret)
3468c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/**
3548c2ecf20Sopenharmony_ci * bootflag_get - Get function for sysfs attribute bootflag
3558c2ecf20Sopenharmony_ci * @dev: the &struct device
3568c2ecf20Sopenharmony_ci * @attr: device attributes
3578c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic ssize_t bootflag_get(struct device *dev,
3608c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buf)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
3638c2ecf20Sopenharmony_ci	int ret;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (ret)
3688c2ecf20Sopenharmony_ci		return ret;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci/**
3748c2ecf20Sopenharmony_ci * bootflag_set - Set function for sysfs attribute bootflag
3758c2ecf20Sopenharmony_ci * @dev: the &struct device
3768c2ecf20Sopenharmony_ci * @attr: device attributes
3778c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
3788c2ecf20Sopenharmony_ci * @count: size of buffer
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_cistatic ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
3818c2ecf20Sopenharmony_ci			    const char *buf, size_t count)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
3848c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
3858c2ecf20Sopenharmony_ci	uint32_t datum;
3868c2ecf20Sopenharmony_ci	int ret;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
3898c2ecf20Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
3908c2ecf20Sopenharmony_ci	if ((ret != 1) || (datum > 1))
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	*((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
3948c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(uint32_t));
3958c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
3968c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_BOOTFLAG);
3978c2ecf20Sopenharmony_ci	if (ret)
3988c2ecf20Sopenharmony_ci		return ret;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return strlen(buf);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci/**
4048c2ecf20Sopenharmony_ci * boottime_get - Get function for sysfs attribute boottime
4058c2ecf20Sopenharmony_ci * @dev: the &struct device
4068c2ecf20Sopenharmony_ci * @attr: device attributes
4078c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
4088c2ecf20Sopenharmony_ci */
4098c2ecf20Sopenharmony_cistatic ssize_t boottime_get(struct device *dev,
4108c2ecf20Sopenharmony_ci			    struct device_attribute *attr, char *buf)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
4138c2ecf20Sopenharmony_ci	int ret;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (ret)
4188c2ecf20Sopenharmony_ci		return ret;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return snprintf(buf, 12, "%d\n", defs.boottime);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/**
4248c2ecf20Sopenharmony_ci * boottime_set - Set function for sysfs attribute boottime
4258c2ecf20Sopenharmony_ci * @dev: the &struct device
4268c2ecf20Sopenharmony_ci * @attr: device attributes
4278c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
4288c2ecf20Sopenharmony_ci * @count: size of buffer
4298c2ecf20Sopenharmony_ci */
4308c2ecf20Sopenharmony_cistatic ssize_t boottime_set(struct device *dev,
4318c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
4348c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
4358c2ecf20Sopenharmony_ci	uint32_t datum;
4368c2ecf20Sopenharmony_ci	int ret;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
4398c2ecf20Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
4408c2ecf20Sopenharmony_ci	if ((ret != 1) || (datum > 255))
4418c2ecf20Sopenharmony_ci		return -EINVAL;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/* A too small boot time will result in the device booting into
4448c2ecf20Sopenharmony_ci	 * standalone (no-host) mode before the host can take control of it,
4458c2ecf20Sopenharmony_ci	 * so the change will be hard to revert.  This may be a desired
4468c2ecf20Sopenharmony_ci	 * feature (e.g to configure a very fast boot time for devices that
4478c2ecf20Sopenharmony_ci	 * will not be attached to a host), but dangerous.  So I'm enforcing a
4488c2ecf20Sopenharmony_ci	 * lower limit of 20 seconds:  remove and recompile the driver if this
4498c2ecf20Sopenharmony_ci	 * does not work for you.
4508c2ecf20Sopenharmony_ci	 */
4518c2ecf20Sopenharmony_ci	datum = (datum < 20) ? 20 : datum;
4528c2ecf20Sopenharmony_ci	cmd.data[0] = datum;
4538c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(uint8_t));
4548c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
4558c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_BOOTTIME);
4568c2ecf20Sopenharmony_ci	if (ret)
4578c2ecf20Sopenharmony_ci		return ret;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return strlen(buf);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/**
4638c2ecf20Sopenharmony_ci * channel_get - Get function for sysfs attribute channel
4648c2ecf20Sopenharmony_ci * @dev: the &struct device
4658c2ecf20Sopenharmony_ci * @attr: device attributes
4668c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
4678c2ecf20Sopenharmony_ci */
4688c2ecf20Sopenharmony_cistatic ssize_t channel_get(struct device *dev,
4698c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
4728c2ecf20Sopenharmony_ci	int ret;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (ret)
4778c2ecf20Sopenharmony_ci		return ret;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci/**
4838c2ecf20Sopenharmony_ci * channel_set - Set function for sysfs attribute channel
4848c2ecf20Sopenharmony_ci * @dev: the &struct device
4858c2ecf20Sopenharmony_ci * @attr: device attributes
4868c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
4878c2ecf20Sopenharmony_ci * @count: size of buffer
4888c2ecf20Sopenharmony_ci */
4898c2ecf20Sopenharmony_cistatic ssize_t channel_set(struct device *dev, struct device_attribute *attr,
4908c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
4938c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
4948c2ecf20Sopenharmony_ci	uint32_t datum;
4958c2ecf20Sopenharmony_ci	int ret;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
4988c2ecf20Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
4998c2ecf20Sopenharmony_ci	if (ret != 1 || datum < 1 || datum > 11)
5008c2ecf20Sopenharmony_ci		return -EINVAL;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	*((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
5038c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(uint16_t));
5048c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
5058c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_DEF_CHANNEL);
5068c2ecf20Sopenharmony_ci	if (ret)
5078c2ecf20Sopenharmony_ci		return ret;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	return strlen(buf);
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/**
5138c2ecf20Sopenharmony_ci * mesh_id_get - Get function for sysfs attribute mesh_id
5148c2ecf20Sopenharmony_ci * @dev: the &struct device
5158c2ecf20Sopenharmony_ci * @attr: device attributes
5168c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
5178c2ecf20Sopenharmony_ci */
5188c2ecf20Sopenharmony_cistatic ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
5198c2ecf20Sopenharmony_ci			   char *buf)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
5228c2ecf20Sopenharmony_ci	int ret;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (ret)
5278c2ecf20Sopenharmony_ci		return ret;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
5308c2ecf20Sopenharmony_ci		dev_err(dev, "inconsistent mesh ID length\n");
5318c2ecf20Sopenharmony_ci		defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);
5358c2ecf20Sopenharmony_ci	buf[defs.meshie.val.mesh_id_len] = '\n';
5368c2ecf20Sopenharmony_ci	buf[defs.meshie.val.mesh_id_len + 1] = '\0';
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return defs.meshie.val.mesh_id_len + 1;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/**
5428c2ecf20Sopenharmony_ci * mesh_id_set - Set function for sysfs attribute mesh_id
5438c2ecf20Sopenharmony_ci * @dev: the &struct device
5448c2ecf20Sopenharmony_ci * @attr: device attributes
5458c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
5468c2ecf20Sopenharmony_ci * @count: size of buffer
5478c2ecf20Sopenharmony_ci */
5488c2ecf20Sopenharmony_cistatic ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
5498c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
5528c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
5538c2ecf20Sopenharmony_ci	struct mrvl_meshie *ie;
5548c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
5558c2ecf20Sopenharmony_ci	int len;
5568c2ecf20Sopenharmony_ci	int ret;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
5598c2ecf20Sopenharmony_ci		return -EINVAL;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
5628c2ecf20Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* fetch all other Information Element parameters */
5658c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* transfer IE elements */
5708c2ecf20Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	len = count - 1;
5738c2ecf20Sopenharmony_ci	memcpy(ie->val.mesh_id, buf, len);
5748c2ecf20Sopenharmony_ci	/* SSID len */
5758c2ecf20Sopenharmony_ci	ie->val.mesh_id_len = len;
5768c2ecf20Sopenharmony_ci	/* IE len */
5778c2ecf20Sopenharmony_ci	ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
5808c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
5818c2ecf20Sopenharmony_ci	if (ret)
5828c2ecf20Sopenharmony_ci		return ret;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return strlen(buf);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci/**
5888c2ecf20Sopenharmony_ci * protocol_id_get - Get function for sysfs attribute protocol_id
5898c2ecf20Sopenharmony_ci * @dev: the &struct device
5908c2ecf20Sopenharmony_ci * @attr: device attributes
5918c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
5928c2ecf20Sopenharmony_ci */
5938c2ecf20Sopenharmony_cistatic ssize_t protocol_id_get(struct device *dev,
5948c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
5978c2ecf20Sopenharmony_ci	int ret;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (ret)
6028c2ecf20Sopenharmony_ci		return ret;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/**
6088c2ecf20Sopenharmony_ci * protocol_id_set - Set function for sysfs attribute protocol_id
6098c2ecf20Sopenharmony_ci * @dev: the &struct device
6108c2ecf20Sopenharmony_ci * @attr: device attributes
6118c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
6128c2ecf20Sopenharmony_ci * @count: size of buffer
6138c2ecf20Sopenharmony_ci */
6148c2ecf20Sopenharmony_cistatic ssize_t protocol_id_set(struct device *dev,
6158c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
6188c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
6198c2ecf20Sopenharmony_ci	struct mrvl_meshie *ie;
6208c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
6218c2ecf20Sopenharmony_ci	uint32_t datum;
6228c2ecf20Sopenharmony_ci	int ret;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
6258c2ecf20Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
6268c2ecf20Sopenharmony_ci	if ((ret != 1) || (datum > 255))
6278c2ecf20Sopenharmony_ci		return -EINVAL;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* fetch all other Information Element parameters */
6308c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	/* transfer IE elements */
6358c2ecf20Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
6368c2ecf20Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
6378c2ecf20Sopenharmony_ci	/* update protocol id */
6388c2ecf20Sopenharmony_ci	ie->val.active_protocol_id = datum;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
6418c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
6428c2ecf20Sopenharmony_ci	if (ret)
6438c2ecf20Sopenharmony_ci		return ret;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	return strlen(buf);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci/**
6498c2ecf20Sopenharmony_ci * metric_id_get - Get function for sysfs attribute metric_id
6508c2ecf20Sopenharmony_ci * @dev: the &struct device
6518c2ecf20Sopenharmony_ci * @attr: device attributes
6528c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
6538c2ecf20Sopenharmony_ci */
6548c2ecf20Sopenharmony_cistatic ssize_t metric_id_get(struct device *dev,
6558c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
6588c2ecf20Sopenharmony_ci	int ret;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	if (ret)
6638c2ecf20Sopenharmony_ci		return ret;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci/**
6698c2ecf20Sopenharmony_ci * metric_id_set - Set function for sysfs attribute metric_id
6708c2ecf20Sopenharmony_ci * @dev: the &struct device
6718c2ecf20Sopenharmony_ci * @attr: device attributes
6728c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
6738c2ecf20Sopenharmony_ci * @count: size of buffer
6748c2ecf20Sopenharmony_ci */
6758c2ecf20Sopenharmony_cistatic ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
6768c2ecf20Sopenharmony_ci			     const char *buf, size_t count)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
6798c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
6808c2ecf20Sopenharmony_ci	struct mrvl_meshie *ie;
6818c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
6828c2ecf20Sopenharmony_ci	uint32_t datum;
6838c2ecf20Sopenharmony_ci	int ret;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
6868c2ecf20Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
6878c2ecf20Sopenharmony_ci	if ((ret != 1) || (datum > 255))
6888c2ecf20Sopenharmony_ci		return -EINVAL;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* fetch all other Information Element parameters */
6918c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* transfer IE elements */
6968c2ecf20Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
6978c2ecf20Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
6988c2ecf20Sopenharmony_ci	/* update metric id */
6998c2ecf20Sopenharmony_ci	ie->val.active_metric_id = datum;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
7028c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
7038c2ecf20Sopenharmony_ci	if (ret)
7048c2ecf20Sopenharmony_ci		return ret;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	return strlen(buf);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/**
7108c2ecf20Sopenharmony_ci * capability_get - Get function for sysfs attribute capability
7118c2ecf20Sopenharmony_ci * @dev: the &struct device
7128c2ecf20Sopenharmony_ci * @attr: device attributes
7138c2ecf20Sopenharmony_ci * @buf: buffer where data will be returned
7148c2ecf20Sopenharmony_ci */
7158c2ecf20Sopenharmony_cistatic ssize_t capability_get(struct device *dev,
7168c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
7198c2ecf20Sopenharmony_ci	int ret;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (ret)
7248c2ecf20Sopenharmony_ci		return ret;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci/**
7308c2ecf20Sopenharmony_ci * capability_set - Set function for sysfs attribute capability
7318c2ecf20Sopenharmony_ci * @dev: the &struct device
7328c2ecf20Sopenharmony_ci * @attr: device attributes
7338c2ecf20Sopenharmony_ci * @buf: buffer that contains new attribute value
7348c2ecf20Sopenharmony_ci * @count: size of buffer
7358c2ecf20Sopenharmony_ci */
7368c2ecf20Sopenharmony_cistatic ssize_t capability_set(struct device *dev, struct device_attribute *attr,
7378c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_config cmd;
7408c2ecf20Sopenharmony_ci	struct mrvl_mesh_defaults defs;
7418c2ecf20Sopenharmony_ci	struct mrvl_meshie *ie;
7428c2ecf20Sopenharmony_ci	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
7438c2ecf20Sopenharmony_ci	uint32_t datum;
7448c2ecf20Sopenharmony_ci	int ret;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
7478c2ecf20Sopenharmony_ci	ret = sscanf(buf, "%d", &datum);
7488c2ecf20Sopenharmony_ci	if ((ret != 1) || (datum > 255))
7498c2ecf20Sopenharmony_ci		return -EINVAL;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/* fetch all other Information Element parameters */
7528c2ecf20Sopenharmony_ci	ret = mesh_get_default_parameters(dev, &defs);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	/* transfer IE elements */
7578c2ecf20Sopenharmony_ci	ie = (struct mrvl_meshie *) &cmd.data[0];
7588c2ecf20Sopenharmony_ci	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
7598c2ecf20Sopenharmony_ci	/* update value */
7608c2ecf20Sopenharmony_ci	ie->val.mesh_capability = datum;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
7638c2ecf20Sopenharmony_ci				   CMD_TYPE_MESH_SET_MESH_IE);
7648c2ecf20Sopenharmony_ci	if (ret)
7658c2ecf20Sopenharmony_ci		return ret;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return strlen(buf);
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
7728c2ecf20Sopenharmony_cistatic DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
7738c2ecf20Sopenharmony_cistatic DEVICE_ATTR(channel, 0644, channel_get, channel_set);
7748c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
7758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
7768c2ecf20Sopenharmony_cistatic DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
7778c2ecf20Sopenharmony_cistatic DEVICE_ATTR(capability, 0644, capability_get, capability_set);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic struct attribute *boot_opts_attrs[] = {
7808c2ecf20Sopenharmony_ci	&dev_attr_bootflag.attr,
7818c2ecf20Sopenharmony_ci	&dev_attr_boottime.attr,
7828c2ecf20Sopenharmony_ci	&dev_attr_channel.attr,
7838c2ecf20Sopenharmony_ci	NULL
7848c2ecf20Sopenharmony_ci};
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic const struct attribute_group boot_opts_group = {
7878c2ecf20Sopenharmony_ci	.name = "boot_options",
7888c2ecf20Sopenharmony_ci	.attrs = boot_opts_attrs,
7898c2ecf20Sopenharmony_ci};
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic struct attribute *mesh_ie_attrs[] = {
7928c2ecf20Sopenharmony_ci	&dev_attr_mesh_id.attr,
7938c2ecf20Sopenharmony_ci	&dev_attr_protocol_id.attr,
7948c2ecf20Sopenharmony_ci	&dev_attr_metric_id.attr,
7958c2ecf20Sopenharmony_ci	&dev_attr_capability.attr,
7968c2ecf20Sopenharmony_ci	NULL
7978c2ecf20Sopenharmony_ci};
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic const struct attribute_group mesh_ie_group = {
8008c2ecf20Sopenharmony_ci	.name = "mesh_ie",
8018c2ecf20Sopenharmony_ci	.attrs = mesh_ie_attrs,
8028c2ecf20Sopenharmony_ci};
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci/***************************************************************************
8068c2ecf20Sopenharmony_ci * Initializing and starting, stopping mesh
8078c2ecf20Sopenharmony_ci */
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci/*
8108c2ecf20Sopenharmony_ci * Check mesh FW version and appropriately send the mesh start
8118c2ecf20Sopenharmony_ci * command
8128c2ecf20Sopenharmony_ci */
8138c2ecf20Sopenharmony_civoid lbs_init_mesh(struct lbs_private *priv)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	/* Determine mesh_fw_ver from fwrelease and fwcapinfo */
8168c2ecf20Sopenharmony_ci	/* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
8178c2ecf20Sopenharmony_ci	/* 5.110.22 have mesh command with 0xa3 command id */
8188c2ecf20Sopenharmony_ci	/* 10.0.0.p0 FW brings in mesh config command with different id */
8198c2ecf20Sopenharmony_ci	/* Check FW version MSB and initialize mesh_fw_ver */
8208c2ecf20Sopenharmony_ci	if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
8218c2ecf20Sopenharmony_ci		/* Enable mesh, if supported, and work out which TLV it uses.
8228c2ecf20Sopenharmony_ci		   0x100 + 291 is an unofficial value used in 5.110.20.pXX
8238c2ecf20Sopenharmony_ci		   0x100 + 37 is the official value used in 5.110.21.pXX
8248c2ecf20Sopenharmony_ci		   but we check them in that order because 20.pXX doesn't
8258c2ecf20Sopenharmony_ci		   give an error -- it just silently fails. */
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		/* 5.110.20.pXX firmware will fail the command if the channel
8288c2ecf20Sopenharmony_ci		   doesn't match the existing channel. But only if the TLV
8298c2ecf20Sopenharmony_ci		   is correct. If the channel is wrong, _BOTH_ versions will
8308c2ecf20Sopenharmony_ci		   give an error to 0x100+291, and allow 0x100+37 to succeed.
8318c2ecf20Sopenharmony_ci		   It's just that 5.110.20.pXX will not have done anything
8328c2ecf20Sopenharmony_ci		   useful */
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci		priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
8358c2ecf20Sopenharmony_ci		if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) {
8368c2ecf20Sopenharmony_ci			priv->mesh_tlv = TLV_TYPE_MESH_ID;
8378c2ecf20Sopenharmony_ci			if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
8388c2ecf20Sopenharmony_ci				priv->mesh_tlv = 0;
8398c2ecf20Sopenharmony_ci		}
8408c2ecf20Sopenharmony_ci	} else
8418c2ecf20Sopenharmony_ci	if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
8428c2ecf20Sopenharmony_ci		(priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
8438c2ecf20Sopenharmony_ci		/* 10.0.0.pXX new firmwares should succeed with TLV
8448c2ecf20Sopenharmony_ci		 * 0x100+37; Do not invoke command with old TLV.
8458c2ecf20Sopenharmony_ci		 */
8468c2ecf20Sopenharmony_ci		priv->mesh_tlv = TLV_TYPE_MESH_ID;
8478c2ecf20Sopenharmony_ci		if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1))
8488c2ecf20Sopenharmony_ci			priv->mesh_tlv = 0;
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/* Stop meshing until interface is brought up */
8528c2ecf20Sopenharmony_ci	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1);
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_civoid lbs_start_mesh(struct lbs_private *priv)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	lbs_add_mesh(priv);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh))
8608c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "cannot register lbs_mesh attribute\n");
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ciint lbs_deinit_mesh(struct lbs_private *priv)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	struct net_device *dev = priv->dev;
8668c2ecf20Sopenharmony_ci	int ret = 0;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	if (priv->mesh_tlv) {
8698c2ecf20Sopenharmony_ci		device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
8708c2ecf20Sopenharmony_ci		ret = 1;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	return ret;
8748c2ecf20Sopenharmony_ci}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci/**
8788c2ecf20Sopenharmony_ci * lbs_mesh_stop - close the mshX interface
8798c2ecf20Sopenharmony_ci *
8808c2ecf20Sopenharmony_ci * @dev:	A pointer to &net_device structure
8818c2ecf20Sopenharmony_ci * returns:	0
8828c2ecf20Sopenharmony_ci */
8838c2ecf20Sopenharmony_cistatic int lbs_mesh_stop(struct net_device *dev)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
8888c2ecf20Sopenharmony_ci		lbs_mesh_get_channel(priv));
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->driver_lock);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
8938c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->driver_lock);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	lbs_update_mcast(priv);
8988c2ecf20Sopenharmony_ci	if (!lbs_iface_active(priv))
8998c2ecf20Sopenharmony_ci		lbs_stop_iface(priv);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	return 0;
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci/**
9058c2ecf20Sopenharmony_ci * lbs_mesh_dev_open - open the mshX interface
9068c2ecf20Sopenharmony_ci *
9078c2ecf20Sopenharmony_ci * @dev:	A pointer to &net_device structure
9088c2ecf20Sopenharmony_ci * returns:	0 or -EBUSY if monitor mode active
9098c2ecf20Sopenharmony_ci */
9108c2ecf20Sopenharmony_cistatic int lbs_mesh_dev_open(struct net_device *dev)
9118c2ecf20Sopenharmony_ci{
9128c2ecf20Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
9138c2ecf20Sopenharmony_ci	int ret = 0;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	if (!priv->iface_running) {
9168c2ecf20Sopenharmony_ci		ret = lbs_start_iface(priv);
9178c2ecf20Sopenharmony_ci		if (ret)
9188c2ecf20Sopenharmony_ci			goto out;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	spin_lock_irq(&priv->driver_lock);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
9248c2ecf20Sopenharmony_ci		ret = -EBUSY;
9258c2ecf20Sopenharmony_ci		spin_unlock_irq(&priv->driver_lock);
9268c2ecf20Sopenharmony_ci		goto out;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	netif_carrier_on(dev);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	if (!priv->tx_pending_len)
9328c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	spin_unlock_irq(&priv->driver_lock);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
9378c2ecf20Sopenharmony_ci		lbs_mesh_get_channel(priv));
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ciout:
9408c2ecf20Sopenharmony_ci	return ret;
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic const struct net_device_ops mesh_netdev_ops = {
9448c2ecf20Sopenharmony_ci	.ndo_open		= lbs_mesh_dev_open,
9458c2ecf20Sopenharmony_ci	.ndo_stop 		= lbs_mesh_stop,
9468c2ecf20Sopenharmony_ci	.ndo_start_xmit		= lbs_hard_start_xmit,
9478c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= lbs_set_mac_address,
9488c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= lbs_set_multicast_list,
9498c2ecf20Sopenharmony_ci};
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci/**
9528c2ecf20Sopenharmony_ci * lbs_add_mesh - add mshX interface
9538c2ecf20Sopenharmony_ci *
9548c2ecf20Sopenharmony_ci * @priv:	A pointer to the &struct lbs_private structure
9558c2ecf20Sopenharmony_ci * returns:	0 if successful, -X otherwise
9568c2ecf20Sopenharmony_ci */
9578c2ecf20Sopenharmony_cistatic int lbs_add_mesh(struct lbs_private *priv)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct net_device *mesh_dev = NULL;
9608c2ecf20Sopenharmony_ci	struct wireless_dev *mesh_wdev;
9618c2ecf20Sopenharmony_ci	int ret = 0;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* Allocate a virtual mesh device */
9648c2ecf20Sopenharmony_ci	mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
9658c2ecf20Sopenharmony_ci	if (!mesh_wdev) {
9668c2ecf20Sopenharmony_ci		lbs_deb_mesh("init mshX wireless device failed\n");
9678c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9688c2ecf20Sopenharmony_ci		goto done;
9698c2ecf20Sopenharmony_ci	}
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup);
9728c2ecf20Sopenharmony_ci	if (!mesh_dev) {
9738c2ecf20Sopenharmony_ci		lbs_deb_mesh("init mshX device failed\n");
9748c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9758c2ecf20Sopenharmony_ci		goto err_free_wdev;
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT;
9798c2ecf20Sopenharmony_ci	mesh_wdev->wiphy = priv->wdev->wiphy;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	if (priv->mesh_tlv) {
9828c2ecf20Sopenharmony_ci		sprintf(mesh_wdev->ssid, "mesh");
9838c2ecf20Sopenharmony_ci		mesh_wdev->mesh_id_up_len = 4;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	mesh_wdev->netdev = mesh_dev;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	mesh_dev->ml_priv = priv;
9898c2ecf20Sopenharmony_ci	mesh_dev->ieee80211_ptr = mesh_wdev;
9908c2ecf20Sopenharmony_ci	priv->mesh_dev = mesh_dev;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	mesh_dev->netdev_ops = &mesh_netdev_ops;
9938c2ecf20Sopenharmony_ci	mesh_dev->ethtool_ops = &lbs_ethtool_ops;
9948c2ecf20Sopenharmony_ci	eth_hw_addr_inherit(mesh_dev, priv->dev);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
9998c2ecf20Sopenharmony_ci	mesh_dev->sysfs_groups[0] = &lbs_mesh_attr_group;
10008c2ecf20Sopenharmony_ci	mesh_dev->sysfs_groups[1] = &boot_opts_group;
10018c2ecf20Sopenharmony_ci	mesh_dev->sysfs_groups[2] = &mesh_ie_group;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	/* Register virtual mesh interface */
10048c2ecf20Sopenharmony_ci	ret = register_netdev(mesh_dev);
10058c2ecf20Sopenharmony_ci	if (ret) {
10068c2ecf20Sopenharmony_ci		pr_err("cannot register mshX virtual interface\n");
10078c2ecf20Sopenharmony_ci		goto err_free_netdev;
10088c2ecf20Sopenharmony_ci	}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* Everything successful */
10118c2ecf20Sopenharmony_ci	ret = 0;
10128c2ecf20Sopenharmony_ci	goto done;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_cierr_free_netdev:
10158c2ecf20Sopenharmony_ci	free_netdev(mesh_dev);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cierr_free_wdev:
10188c2ecf20Sopenharmony_ci	kfree(mesh_wdev);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cidone:
10218c2ecf20Sopenharmony_ci	return ret;
10228c2ecf20Sopenharmony_ci}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_civoid lbs_remove_mesh(struct lbs_private *priv)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	struct net_device *mesh_dev;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	mesh_dev = priv->mesh_dev;
10298c2ecf20Sopenharmony_ci	if (!mesh_dev)
10308c2ecf20Sopenharmony_ci		return;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	netif_stop_queue(mesh_dev);
10338c2ecf20Sopenharmony_ci	netif_carrier_off(mesh_dev);
10348c2ecf20Sopenharmony_ci	unregister_netdev(mesh_dev);
10358c2ecf20Sopenharmony_ci	priv->mesh_dev = NULL;
10368c2ecf20Sopenharmony_ci	kfree(mesh_dev->ieee80211_ptr);
10378c2ecf20Sopenharmony_ci	free_netdev(mesh_dev);
10388c2ecf20Sopenharmony_ci}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci/***************************************************************************
10428c2ecf20Sopenharmony_ci * Sending and receiving
10438c2ecf20Sopenharmony_ci */
10448c2ecf20Sopenharmony_cistruct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
10458c2ecf20Sopenharmony_ci	struct net_device *dev, struct rxpd *rxpd)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	if (priv->mesh_dev) {
10488c2ecf20Sopenharmony_ci		if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
10498c2ecf20Sopenharmony_ci			if (rxpd->rx_control & RxPD_MESH_FRAME)
10508c2ecf20Sopenharmony_ci				dev = priv->mesh_dev;
10518c2ecf20Sopenharmony_ci		} else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
10528c2ecf20Sopenharmony_ci			if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
10538c2ecf20Sopenharmony_ci				dev = priv->mesh_dev;
10548c2ecf20Sopenharmony_ci		}
10558c2ecf20Sopenharmony_ci	}
10568c2ecf20Sopenharmony_ci	return dev;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_civoid lbs_mesh_set_txpd(struct lbs_private *priv,
10618c2ecf20Sopenharmony_ci	struct net_device *dev, struct txpd *txpd)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	if (dev == priv->mesh_dev) {
10648c2ecf20Sopenharmony_ci		if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
10658c2ecf20Sopenharmony_ci			txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
10668c2ecf20Sopenharmony_ci		else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
10678c2ecf20Sopenharmony_ci			txpd->u.bss.bss_num = MESH_IFACE_ID;
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci/***************************************************************************
10738c2ecf20Sopenharmony_ci * Ethtool related
10748c2ecf20Sopenharmony_ci */
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic const char mesh_stat_strings[MESH_STATS_NUM][ETH_GSTRING_LEN] = {
10778c2ecf20Sopenharmony_ci	"drop_duplicate_bcast",
10788c2ecf20Sopenharmony_ci	"drop_ttl_zero",
10798c2ecf20Sopenharmony_ci	"drop_no_fwd_route",
10808c2ecf20Sopenharmony_ci	"drop_no_buffers",
10818c2ecf20Sopenharmony_ci	"fwded_unicast_cnt",
10828c2ecf20Sopenharmony_ci	"fwded_bcast_cnt",
10838c2ecf20Sopenharmony_ci	"drop_blind_table",
10848c2ecf20Sopenharmony_ci	"tx_failed_cnt"
10858c2ecf20Sopenharmony_ci};
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_civoid lbs_mesh_ethtool_get_stats(struct net_device *dev,
10888c2ecf20Sopenharmony_ci	struct ethtool_stats *stats, uint64_t *data)
10898c2ecf20Sopenharmony_ci{
10908c2ecf20Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
10918c2ecf20Sopenharmony_ci	struct cmd_ds_mesh_access mesh_access;
10928c2ecf20Sopenharmony_ci	int ret;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	/* Get Mesh Statistics */
10958c2ecf20Sopenharmony_ci	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	if (ret) {
10988c2ecf20Sopenharmony_ci		memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
10998c2ecf20Sopenharmony_ci		return;
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
11038c2ecf20Sopenharmony_ci	priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
11048c2ecf20Sopenharmony_ci	priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
11058c2ecf20Sopenharmony_ci	priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
11068c2ecf20Sopenharmony_ci	priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
11078c2ecf20Sopenharmony_ci	priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
11088c2ecf20Sopenharmony_ci	priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
11098c2ecf20Sopenharmony_ci	priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	data[0] = priv->mstats.fwd_drop_rbt;
11128c2ecf20Sopenharmony_ci	data[1] = priv->mstats.fwd_drop_ttl;
11138c2ecf20Sopenharmony_ci	data[2] = priv->mstats.fwd_drop_noroute;
11148c2ecf20Sopenharmony_ci	data[3] = priv->mstats.fwd_drop_nobuf;
11158c2ecf20Sopenharmony_ci	data[4] = priv->mstats.fwd_unicast_cnt;
11168c2ecf20Sopenharmony_ci	data[5] = priv->mstats.fwd_bcast_cnt;
11178c2ecf20Sopenharmony_ci	data[6] = priv->mstats.drop_blind;
11188c2ecf20Sopenharmony_ci	data[7] = priv->mstats.tx_failed_cnt;
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ciint lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)
11228c2ecf20Sopenharmony_ci{
11238c2ecf20Sopenharmony_ci	struct lbs_private *priv = dev->ml_priv;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
11268c2ecf20Sopenharmony_ci		return MESH_STATS_NUM;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_civoid lbs_mesh_ethtool_get_strings(struct net_device *dev,
11328c2ecf20Sopenharmony_ci	uint32_t stringset, uint8_t *s)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	switch (stringset) {
11358c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
11368c2ecf20Sopenharmony_ci		memcpy(s, mesh_stat_strings, sizeof(mesh_stat_strings));
11378c2ecf20Sopenharmony_ci		break;
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci}
1140