xref: /openbmc/linux/net/ethtool/eee.c (revision 18afb028)
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 const struct nla_policy ethnl_eee_get_policy[] = {
23 	[ETHTOOL_A_EEE_HEADER]		=
24 		NLA_POLICY_NESTED(ethnl_header_policy),
25 };
26 
27 static 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 
46 static 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 
81 static 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 
113 const 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 
122 static int
123 ethnl_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 
130 static int
131 ethnl_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 
160 const 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