xref: /linux/net/ethtool/eee.c (revision 666ed8bfd1de3b091cf32ca03b651757dd86cfff)
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 
10 struct eee_req_info {
11 	struct ethnl_req_info		base;
12 };
13 
14 struct 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 
22 static const struct nla_policy
23 eee_get_policy[ETHTOOL_A_EEE_MAX + 1] = {
24 	[ETHTOOL_A_EEE_UNSPEC]		= { .type = NLA_REJECT },
25 	[ETHTOOL_A_EEE_HEADER]		= { .type = NLA_NESTED },
26 	[ETHTOOL_A_EEE_MODES_OURS]	= { .type = NLA_REJECT },
27 	[ETHTOOL_A_EEE_MODES_PEER]	= { .type = NLA_REJECT },
28 	[ETHTOOL_A_EEE_ACTIVE]		= { .type = NLA_REJECT },
29 	[ETHTOOL_A_EEE_ENABLED]		= { .type = NLA_REJECT },
30 	[ETHTOOL_A_EEE_TX_LPI_ENABLED]	= { .type = NLA_REJECT },
31 	[ETHTOOL_A_EEE_TX_LPI_TIMER]	= { .type = NLA_REJECT },
32 };
33 
34 static int eee_prepare_data(const struct ethnl_req_info *req_base,
35 			    struct ethnl_reply_data *reply_base,
36 			    struct genl_info *info)
37 {
38 	struct eee_reply_data *data = EEE_REPDATA(reply_base);
39 	struct net_device *dev = reply_base->dev;
40 	int ret;
41 
42 	if (!dev->ethtool_ops->get_eee)
43 		return -EOPNOTSUPP;
44 	ret = ethnl_ops_begin(dev);
45 	if (ret < 0)
46 		return ret;
47 	ret = dev->ethtool_ops->get_eee(dev, &data->eee);
48 	ethnl_ops_complete(dev);
49 
50 	return ret;
51 }
52 
53 static int eee_reply_size(const struct ethnl_req_info *req_base,
54 			  const struct ethnl_reply_data *reply_base)
55 {
56 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
57 	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
58 	const struct ethtool_eee *eee = &data->eee;
59 	int len = 0;
60 	int ret;
61 
62 	BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
63 		     EEE_MODES_COUNT);
64 	BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
65 		     EEE_MODES_COUNT);
66 
67 	/* MODES_OURS */
68 	ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
69 				  EEE_MODES_COUNT, link_mode_names, compact);
70 	if (ret < 0)
71 		return ret;
72 	len += ret;
73 	/* MODES_PEERS */
74 	ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
75 				  EEE_MODES_COUNT, link_mode_names, compact);
76 	if (ret < 0)
77 		return ret;
78 	len += ret;
79 
80 	len += nla_total_size(sizeof(u8)) +	/* _EEE_ACTIVE */
81 	       nla_total_size(sizeof(u8)) +	/* _EEE_ENABLED */
82 	       nla_total_size(sizeof(u8)) +	/* _EEE_TX_LPI_ENABLED */
83 	       nla_total_size(sizeof(u32));	/* _EEE_TX_LPI_TIMER */
84 
85 	return len;
86 }
87 
88 static int eee_fill_reply(struct sk_buff *skb,
89 			  const struct ethnl_req_info *req_base,
90 			  const struct ethnl_reply_data *reply_base)
91 {
92 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
93 	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
94 	const struct ethtool_eee *eee = &data->eee;
95 	int ret;
96 
97 	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
98 				 &eee->advertised, &eee->supported,
99 				 EEE_MODES_COUNT, link_mode_names, compact);
100 	if (ret < 0)
101 		return ret;
102 	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
103 				 &eee->lp_advertised, NULL, EEE_MODES_COUNT,
104 				 link_mode_names, compact);
105 	if (ret < 0)
106 		return ret;
107 
108 	if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
109 	    nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
110 	    nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
111 		       !!eee->tx_lpi_enabled) ||
112 	    nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
113 		return -EMSGSIZE;
114 
115 	return 0;
116 }
117 
118 const struct ethnl_request_ops ethnl_eee_request_ops = {
119 	.request_cmd		= ETHTOOL_MSG_EEE_GET,
120 	.reply_cmd		= ETHTOOL_MSG_EEE_GET_REPLY,
121 	.hdr_attr		= ETHTOOL_A_EEE_HEADER,
122 	.max_attr		= ETHTOOL_A_EEE_MAX,
123 	.req_info_size		= sizeof(struct eee_req_info),
124 	.reply_data_size	= sizeof(struct eee_reply_data),
125 	.request_policy		= eee_get_policy,
126 
127 	.prepare_data		= eee_prepare_data,
128 	.reply_size		= eee_reply_size,
129 	.fill_reply		= eee_fill_reply,
130 };
131 
132 /* EEE_SET */
133 
134 static const struct nla_policy
135 eee_set_policy[ETHTOOL_A_EEE_MAX + 1] = {
136 	[ETHTOOL_A_EEE_UNSPEC]		= { .type = NLA_REJECT },
137 	[ETHTOOL_A_EEE_HEADER]		= { .type = NLA_NESTED },
138 	[ETHTOOL_A_EEE_MODES_OURS]	= { .type = NLA_NESTED },
139 	[ETHTOOL_A_EEE_MODES_PEER]	= { .type = NLA_REJECT },
140 	[ETHTOOL_A_EEE_ACTIVE]		= { .type = NLA_REJECT },
141 	[ETHTOOL_A_EEE_ENABLED]		= { .type = NLA_U8 },
142 	[ETHTOOL_A_EEE_TX_LPI_ENABLED]	= { .type = NLA_U8 },
143 	[ETHTOOL_A_EEE_TX_LPI_TIMER]	= { .type = NLA_U32 },
144 };
145 
146 int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info)
147 {
148 	struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1];
149 	struct ethtool_eee eee = {};
150 	struct ethnl_req_info req_info = {};
151 	const struct ethtool_ops *ops;
152 	struct net_device *dev;
153 	bool mod = false;
154 	int ret;
155 
156 	ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ETHTOOL_A_EEE_MAX,
157 			  eee_set_policy, info->extack);
158 	if (ret < 0)
159 		return ret;
160 	ret = ethnl_parse_header_dev_get(&req_info,
161 					 tb[ETHTOOL_A_EEE_HEADER],
162 					 genl_info_net(info), info->extack,
163 					 true);
164 	if (ret < 0)
165 		return ret;
166 	dev = req_info.dev;
167 	ops = dev->ethtool_ops;
168 	ret = -EOPNOTSUPP;
169 	if (!ops->get_eee || !ops->set_eee)
170 		goto out_dev;
171 
172 	rtnl_lock();
173 	ret = ethnl_ops_begin(dev);
174 	if (ret < 0)
175 		goto out_rtnl;
176 	ret = ops->get_eee(dev, &eee);
177 	if (ret < 0)
178 		goto out_ops;
179 
180 	ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
181 				    tb[ETHTOOL_A_EEE_MODES_OURS],
182 				    link_mode_names, info->extack, &mod);
183 	if (ret < 0)
184 		goto out_ops;
185 	ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
186 	ethnl_update_bool32(&eee.tx_lpi_enabled,
187 			    tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
188 	ethnl_update_bool32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
189 			    &mod);
190 	ret = 0;
191 	if (!mod)
192 		goto out_ops;
193 
194 	ret = dev->ethtool_ops->set_eee(dev, &eee);
195 	if (ret < 0)
196 		goto out_ops;
197 	ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL);
198 
199 out_ops:
200 	ethnl_ops_complete(dev);
201 out_rtnl:
202 	rtnl_unlock();
203 out_dev:
204 	dev_put(dev);
205 	return ret;
206 }
207