162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "netlink.h"
462306a36Sopenharmony_ci#include "common.h"
562306a36Sopenharmony_ci#include "bitset.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define EEE_MODES_COUNT \
862306a36Sopenharmony_ci	(sizeof_field(struct ethtool_eee, supported) * BITS_PER_BYTE)
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistruct eee_req_info {
1162306a36Sopenharmony_ci	struct ethnl_req_info		base;
1262306a36Sopenharmony_ci};
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct eee_reply_data {
1562306a36Sopenharmony_ci	struct ethnl_reply_data		base;
1662306a36Sopenharmony_ci	struct ethtool_eee		eee;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define EEE_REPDATA(__reply_base) \
2062306a36Sopenharmony_ci	container_of(__reply_base, struct eee_reply_data, base)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciconst struct nla_policy ethnl_eee_get_policy[] = {
2362306a36Sopenharmony_ci	[ETHTOOL_A_EEE_HEADER]		=
2462306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int eee_prepare_data(const struct ethnl_req_info *req_base,
2862306a36Sopenharmony_ci			    struct ethnl_reply_data *reply_base,
2962306a36Sopenharmony_ci			    const struct genl_info *info)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct eee_reply_data *data = EEE_REPDATA(reply_base);
3262306a36Sopenharmony_ci	struct net_device *dev = reply_base->dev;
3362306a36Sopenharmony_ci	int ret;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (!dev->ethtool_ops->get_eee)
3662306a36Sopenharmony_ci		return -EOPNOTSUPP;
3762306a36Sopenharmony_ci	ret = ethnl_ops_begin(dev);
3862306a36Sopenharmony_ci	if (ret < 0)
3962306a36Sopenharmony_ci		return ret;
4062306a36Sopenharmony_ci	ret = dev->ethtool_ops->get_eee(dev, &data->eee);
4162306a36Sopenharmony_ci	ethnl_ops_complete(dev);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return ret;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int eee_reply_size(const struct ethnl_req_info *req_base,
4762306a36Sopenharmony_ci			  const struct ethnl_reply_data *reply_base)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
5062306a36Sopenharmony_ci	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
5162306a36Sopenharmony_ci	const struct ethtool_eee *eee = &data->eee;
5262306a36Sopenharmony_ci	int len = 0;
5362306a36Sopenharmony_ci	int ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
5662306a36Sopenharmony_ci		     EEE_MODES_COUNT);
5762306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
5862306a36Sopenharmony_ci		     EEE_MODES_COUNT);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* MODES_OURS */
6162306a36Sopenharmony_ci	ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
6262306a36Sopenharmony_ci				  EEE_MODES_COUNT, link_mode_names, compact);
6362306a36Sopenharmony_ci	if (ret < 0)
6462306a36Sopenharmony_ci		return ret;
6562306a36Sopenharmony_ci	len += ret;
6662306a36Sopenharmony_ci	/* MODES_PEERS */
6762306a36Sopenharmony_ci	ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
6862306a36Sopenharmony_ci				  EEE_MODES_COUNT, link_mode_names, compact);
6962306a36Sopenharmony_ci	if (ret < 0)
7062306a36Sopenharmony_ci		return ret;
7162306a36Sopenharmony_ci	len += ret;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	len += nla_total_size(sizeof(u8)) +	/* _EEE_ACTIVE */
7462306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +	/* _EEE_ENABLED */
7562306a36Sopenharmony_ci	       nla_total_size(sizeof(u8)) +	/* _EEE_TX_LPI_ENABLED */
7662306a36Sopenharmony_ci	       nla_total_size(sizeof(u32));	/* _EEE_TX_LPI_TIMER */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return len;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int eee_fill_reply(struct sk_buff *skb,
8262306a36Sopenharmony_ci			  const struct ethnl_req_info *req_base,
8362306a36Sopenharmony_ci			  const struct ethnl_reply_data *reply_base)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
8662306a36Sopenharmony_ci	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
8762306a36Sopenharmony_ci	const struct ethtool_eee *eee = &data->eee;
8862306a36Sopenharmony_ci	int ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
9162306a36Sopenharmony_ci				 &eee->advertised, &eee->supported,
9262306a36Sopenharmony_ci				 EEE_MODES_COUNT, link_mode_names, compact);
9362306a36Sopenharmony_ci	if (ret < 0)
9462306a36Sopenharmony_ci		return ret;
9562306a36Sopenharmony_ci	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
9662306a36Sopenharmony_ci				 &eee->lp_advertised, NULL, EEE_MODES_COUNT,
9762306a36Sopenharmony_ci				 link_mode_names, compact);
9862306a36Sopenharmony_ci	if (ret < 0)
9962306a36Sopenharmony_ci		return ret;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
10262306a36Sopenharmony_ci	    nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
10362306a36Sopenharmony_ci	    nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
10462306a36Sopenharmony_ci		       !!eee->tx_lpi_enabled) ||
10562306a36Sopenharmony_ci	    nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
10662306a36Sopenharmony_ci		return -EMSGSIZE;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* EEE_SET */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciconst struct nla_policy ethnl_eee_set_policy[] = {
11462306a36Sopenharmony_ci	[ETHTOOL_A_EEE_HEADER]		=
11562306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
11662306a36Sopenharmony_ci	[ETHTOOL_A_EEE_MODES_OURS]	= { .type = NLA_NESTED },
11762306a36Sopenharmony_ci	[ETHTOOL_A_EEE_ENABLED]		= { .type = NLA_U8 },
11862306a36Sopenharmony_ci	[ETHTOOL_A_EEE_TX_LPI_ENABLED]	= { .type = NLA_U8 },
11962306a36Sopenharmony_ci	[ETHTOOL_A_EEE_TX_LPI_TIMER]	= { .type = NLA_U32 },
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int
12362306a36Sopenharmony_ciethnl_set_eee_validate(struct ethnl_req_info *req_info, struct genl_info *info)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return ops->get_eee && ops->set_eee ? 1 : -EOPNOTSUPP;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int
13162306a36Sopenharmony_ciethnl_set_eee(struct ethnl_req_info *req_info, struct genl_info *info)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct net_device *dev = req_info->dev;
13462306a36Sopenharmony_ci	struct nlattr **tb = info->attrs;
13562306a36Sopenharmony_ci	struct ethtool_eee eee = {};
13662306a36Sopenharmony_ci	bool mod = false;
13762306a36Sopenharmony_ci	int ret;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ret = dev->ethtool_ops->get_eee(dev, &eee);
14062306a36Sopenharmony_ci	if (ret < 0)
14162306a36Sopenharmony_ci		return ret;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
14462306a36Sopenharmony_ci				    tb[ETHTOOL_A_EEE_MODES_OURS],
14562306a36Sopenharmony_ci				    link_mode_names, info->extack, &mod);
14662306a36Sopenharmony_ci	if (ret < 0)
14762306a36Sopenharmony_ci		return ret;
14862306a36Sopenharmony_ci	ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
14962306a36Sopenharmony_ci	ethnl_update_bool32(&eee.tx_lpi_enabled,
15062306a36Sopenharmony_ci			    tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
15162306a36Sopenharmony_ci	ethnl_update_u32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
15262306a36Sopenharmony_ci			 &mod);
15362306a36Sopenharmony_ci	if (!mod)
15462306a36Sopenharmony_ci		return 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = dev->ethtool_ops->set_eee(dev, &eee);
15762306a36Sopenharmony_ci	return ret < 0 ? ret : 1;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_eee_request_ops = {
16162306a36Sopenharmony_ci	.request_cmd		= ETHTOOL_MSG_EEE_GET,
16262306a36Sopenharmony_ci	.reply_cmd		= ETHTOOL_MSG_EEE_GET_REPLY,
16362306a36Sopenharmony_ci	.hdr_attr		= ETHTOOL_A_EEE_HEADER,
16462306a36Sopenharmony_ci	.req_info_size		= sizeof(struct eee_req_info),
16562306a36Sopenharmony_ci	.reply_data_size	= sizeof(struct eee_reply_data),
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	.prepare_data		= eee_prepare_data,
16862306a36Sopenharmony_ci	.reply_size		= eee_reply_size,
16962306a36Sopenharmony_ci	.fill_reply		= eee_fill_reply,
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	.set_validate		= ethnl_set_eee_validate,
17262306a36Sopenharmony_ci	.set			= ethnl_set_eee,
17362306a36Sopenharmony_ci	.set_ntf_cmd		= ETHTOOL_MSG_EEE_NTF,
17462306a36Sopenharmony_ci};
175