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