xref: /kernel/linux/linux-6.6/net/ethtool/eee.c (revision 62306a36)
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include "netlink.h"
4#include "common.h"
5#include "bitset.h"
6
7#define EEE_MODES_COUNT \
8	(sizeof_field(struct ethtool_eee, supported) * BITS_PER_BYTE)
9
10struct eee_req_info {
11	struct ethnl_req_info		base;
12};
13
14struct eee_reply_data {
15	struct ethnl_reply_data		base;
16	struct ethtool_eee		eee;
17};
18
19#define EEE_REPDATA(__reply_base) \
20	container_of(__reply_base, struct eee_reply_data, base)
21
22const struct nla_policy ethnl_eee_get_policy[] = {
23	[ETHTOOL_A_EEE_HEADER]		=
24		NLA_POLICY_NESTED(ethnl_header_policy),
25};
26
27static int eee_prepare_data(const struct ethnl_req_info *req_base,
28			    struct ethnl_reply_data *reply_base,
29			    const struct genl_info *info)
30{
31	struct eee_reply_data *data = EEE_REPDATA(reply_base);
32	struct net_device *dev = reply_base->dev;
33	int ret;
34
35	if (!dev->ethtool_ops->get_eee)
36		return -EOPNOTSUPP;
37	ret = ethnl_ops_begin(dev);
38	if (ret < 0)
39		return ret;
40	ret = dev->ethtool_ops->get_eee(dev, &data->eee);
41	ethnl_ops_complete(dev);
42
43	return ret;
44}
45
46static int eee_reply_size(const struct ethnl_req_info *req_base,
47			  const struct ethnl_reply_data *reply_base)
48{
49	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
50	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
51	const struct ethtool_eee *eee = &data->eee;
52	int len = 0;
53	int ret;
54
55	BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
56		     EEE_MODES_COUNT);
57	BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
58		     EEE_MODES_COUNT);
59
60	/* MODES_OURS */
61	ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
62				  EEE_MODES_COUNT, link_mode_names, compact);
63	if (ret < 0)
64		return ret;
65	len += ret;
66	/* MODES_PEERS */
67	ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
68				  EEE_MODES_COUNT, link_mode_names, compact);
69	if (ret < 0)
70		return ret;
71	len += ret;
72
73	len += nla_total_size(sizeof(u8)) +	/* _EEE_ACTIVE */
74	       nla_total_size(sizeof(u8)) +	/* _EEE_ENABLED */
75	       nla_total_size(sizeof(u8)) +	/* _EEE_TX_LPI_ENABLED */
76	       nla_total_size(sizeof(u32));	/* _EEE_TX_LPI_TIMER */
77
78	return len;
79}
80
81static int eee_fill_reply(struct sk_buff *skb,
82			  const struct ethnl_req_info *req_base,
83			  const struct ethnl_reply_data *reply_base)
84{
85	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
86	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
87	const struct ethtool_eee *eee = &data->eee;
88	int ret;
89
90	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
91				 &eee->advertised, &eee->supported,
92				 EEE_MODES_COUNT, link_mode_names, compact);
93	if (ret < 0)
94		return ret;
95	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
96				 &eee->lp_advertised, NULL, EEE_MODES_COUNT,
97				 link_mode_names, compact);
98	if (ret < 0)
99		return ret;
100
101	if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
102	    nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
103	    nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
104		       !!eee->tx_lpi_enabled) ||
105	    nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
106		return -EMSGSIZE;
107
108	return 0;
109}
110
111/* EEE_SET */
112
113const struct nla_policy ethnl_eee_set_policy[] = {
114	[ETHTOOL_A_EEE_HEADER]		=
115		NLA_POLICY_NESTED(ethnl_header_policy),
116	[ETHTOOL_A_EEE_MODES_OURS]	= { .type = NLA_NESTED },
117	[ETHTOOL_A_EEE_ENABLED]		= { .type = NLA_U8 },
118	[ETHTOOL_A_EEE_TX_LPI_ENABLED]	= { .type = NLA_U8 },
119	[ETHTOOL_A_EEE_TX_LPI_TIMER]	= { .type = NLA_U32 },
120};
121
122static int
123ethnl_set_eee_validate(struct ethnl_req_info *req_info, struct genl_info *info)
124{
125	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
126
127	return ops->get_eee && ops->set_eee ? 1 : -EOPNOTSUPP;
128}
129
130static int
131ethnl_set_eee(struct ethnl_req_info *req_info, struct genl_info *info)
132{
133	struct net_device *dev = req_info->dev;
134	struct nlattr **tb = info->attrs;
135	struct ethtool_eee eee = {};
136	bool mod = false;
137	int ret;
138
139	ret = dev->ethtool_ops->get_eee(dev, &eee);
140	if (ret < 0)
141		return ret;
142
143	ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
144				    tb[ETHTOOL_A_EEE_MODES_OURS],
145				    link_mode_names, info->extack, &mod);
146	if (ret < 0)
147		return ret;
148	ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
149	ethnl_update_bool32(&eee.tx_lpi_enabled,
150			    tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
151	ethnl_update_u32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
152			 &mod);
153	if (!mod)
154		return 0;
155
156	ret = dev->ethtool_ops->set_eee(dev, &eee);
157	return ret < 0 ? ret : 1;
158}
159
160const struct ethnl_request_ops ethnl_eee_request_ops = {
161	.request_cmd		= ETHTOOL_MSG_EEE_GET,
162	.reply_cmd		= ETHTOOL_MSG_EEE_GET_REPLY,
163	.hdr_attr		= ETHTOOL_A_EEE_HEADER,
164	.req_info_size		= sizeof(struct eee_req_info),
165	.reply_data_size	= sizeof(struct eee_reply_data),
166
167	.prepare_data		= eee_prepare_data,
168	.reply_size		= eee_reply_size,
169	.fill_reply		= eee_fill_reply,
170
171	.set_validate		= ethnl_set_eee_validate,
172	.set			= ethnl_set_eee,
173	.set_ntf_cmd		= ETHTOOL_MSG_EEE_NTF,
174};
175