ethtool.c 5.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/delay.h>

#include "host.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
#include "wext.h"
10 11
#include "cmd.h"

12 13 14 15 16 17 18
static const char * mesh_stat_strings[]= {
			"drop_duplicate_bcast",
			"drop_ttl_zero",
			"drop_no_fwd_route",
			"drop_no_buffers",
			"fwded_unicast_cnt",
			"fwded_bcast_cnt",
19 20
			"drop_blind_table",
			"tx_failed_cnt"
21 22
};

23
static void lbs_ethtool_get_drvinfo(struct net_device *dev,
24 25
					 struct ethtool_drvinfo *info)
{
26
	struct lbs_private *priv = (struct lbs_private *) dev->priv;
27 28
	char fwver[32];

29
	lbs_get_fwversion(priv, fwver, sizeof(fwver) - 1);
30 31

	strcpy(info->driver, "libertas");
32
	strcpy(info->version, lbs_driver_version);
33 34 35 36 37 38
	strcpy(info->fw_version, fwver);
}

/* All 8388 parts have 16KiB EEPROM size at the time of writing.
 * In case that changes this needs fixing.
 */
39
#define LBS_EEPROM_LEN 16384
40

41
static int lbs_ethtool_get_eeprom_len(struct net_device *dev)
42
{
43
	return LBS_EEPROM_LEN;
44 45
}

46
static int lbs_ethtool_get_eeprom(struct net_device *dev,
47 48
                                  struct ethtool_eeprom *eeprom, u8 * bytes)
{
49
	struct lbs_private *priv = (struct lbs_private *) dev->priv;
50
	struct cmd_ds_802_11_eeprom_access cmd;
51 52
	int ret;

53
	lbs_deb_enter(LBS_DEB_ETHTOOL);
54

55 56 57 58
	if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN ||
	    eeprom->len > LBS_EEPROM_READ_LEN) {
		ret = -EINVAL;
		goto out;
59 60
	}

61 62 63 64 65 66 67 68 69 70 71
	cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) -
		LBS_EEPROM_READ_LEN + eeprom->len);
	cmd.action = cpu_to_le16(CMD_ACT_GET);
	cmd.offset = cpu_to_le16(eeprom->offset);
	cmd.len    = cpu_to_le16(eeprom->len);
	ret = lbs_cmd_with_response(priv, CMD_802_11_EEPROM_ACCESS, &cmd);
	if (!ret)
		memcpy(bytes, cmd.value, eeprom->len);

out:
	lbs_deb_leave_args(LBS_DEB_ETHTOOL, "ret %d", ret);
72
        return ret;
73 74
}

75
static void lbs_ethtool_get_stats(struct net_device * dev,
76 77
				struct ethtool_stats * stats, u64 * data)
{
78
	struct lbs_private *priv = dev->priv;
79
	struct cmd_ds_mesh_access mesh_access;
80
	int ret;
81

82 83
	lbs_deb_enter(LBS_DEB_ETHTOOL);

84
	/* Get Mesh Statistics */
85
	ret = lbs_prepare_and_send_command(priv,
86 87
			CMD_MESH_ACCESS, CMD_ACT_MESH_GET_STATS,
			CMD_OPTION_WAITFORRSP, 0, &mesh_access);
88

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
	if (ret)
		return;

	priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
	priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
	priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
	priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
	priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
	priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
	priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
	priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);

	data[0] = priv->mstats.fwd_drop_rbt;
	data[1] = priv->mstats.fwd_drop_ttl;
	data[2] = priv->mstats.fwd_drop_noroute;
	data[3] = priv->mstats.fwd_drop_nobuf;
	data[4] = priv->mstats.fwd_unicast_cnt;
	data[5] = priv->mstats.fwd_bcast_cnt;
	data[6] = priv->mstats.drop_blind;
	data[7] = priv->mstats.tx_failed_cnt;
109

110 111
	lbs_deb_enter(LBS_DEB_ETHTOOL);
}
112

113
static int lbs_ethtool_get_sset_count(struct net_device * dev, int sset)
114 115 116 117 118 119 120
{
	switch (sset) {
	case ETH_SS_STATS:
		return MESH_STATS_NUM;
	default:
		return -EOPNOTSUPP;
	}
121 122
}

123
static void lbs_ethtool_get_strings(struct net_device *dev,
124 125 126 127 128
					  u32 stringset,
					  u8 * s)
{
	int i;

129 130
	lbs_deb_enter(LBS_DEB_ETHTOOL);

131 132 133 134 135 136 137 138 139
	switch (stringset) {
        case ETH_SS_STATS:
		for (i=0; i < MESH_STATS_NUM; i++) {
			memcpy(s + i * ETH_GSTRING_LEN,
					mesh_stat_strings[i],
					ETH_GSTRING_LEN);
		}
		break;
        }
140
	lbs_deb_enter(LBS_DEB_ETHTOOL);
141 142
}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
static void lbs_ethtool_get_wol(struct net_device *dev,
				struct ethtool_wolinfo *wol)
{
	struct lbs_private *priv = dev->priv;

	if (priv->wol_criteria == 0xffffffff) {
		/* Interface driver didn't configure wake */
		wol->supported = wol->wolopts = 0;
		return;
	}

	wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY;

	if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA)
		wol->wolopts |= WAKE_UCAST;
	if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA)
		wol->wolopts |= WAKE_MCAST;
	if (priv->wol_criteria & EHS_WAKE_ON_BROADCAST_DATA)
		wol->wolopts |= WAKE_BCAST;
	if (priv->wol_criteria & EHS_WAKE_ON_MAC_EVENT)
		wol->wolopts |= WAKE_PHY;
}

static int lbs_ethtool_set_wol(struct net_device *dev,
			       struct ethtool_wolinfo *wol)
{
	struct lbs_private *priv = dev->priv;
	uint32_t criteria = 0;

	if (priv->wol_criteria == 0xffffffff && wol->wolopts)
		return -EOPNOTSUPP;

	if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY))
		return -EOPNOTSUPP;

	if (wol->wolopts & WAKE_UCAST) criteria |= EHS_WAKE_ON_UNICAST_DATA;
	if (wol->wolopts & WAKE_MCAST) criteria |= EHS_WAKE_ON_MULTICAST_DATA;
	if (wol->wolopts & WAKE_BCAST) criteria |= EHS_WAKE_ON_BROADCAST_DATA;
	if (wol->wolopts & WAKE_PHY)   criteria |= EHS_WAKE_ON_MAC_EVENT;

	return lbs_host_sleep_cfg(priv, criteria);
}

186 187 188 189 190 191 192
struct ethtool_ops lbs_ethtool_ops = {
	.get_drvinfo = lbs_ethtool_get_drvinfo,
	.get_eeprom =  lbs_ethtool_get_eeprom,
	.get_eeprom_len = lbs_ethtool_get_eeprom_len,
	.get_sset_count = lbs_ethtool_get_sset_count,
	.get_ethtool_stats = lbs_ethtool_get_stats,
	.get_strings = lbs_ethtool_get_strings,
193 194
	.get_wol = lbs_ethtool_get_wol,
	.set_wol = lbs_ethtool_set_wol,
195 196
};