xref: /openbmc/linux/net/ethtool/eee.c (revision f946270d)
1b7eeefe7SMichal Kubecek // SPDX-License-Identifier: GPL-2.0-only
2b7eeefe7SMichal Kubecek 
3b7eeefe7SMichal Kubecek #include "netlink.h"
4b7eeefe7SMichal Kubecek #include "common.h"
5b7eeefe7SMichal Kubecek #include "bitset.h"
6b7eeefe7SMichal Kubecek 
7b7eeefe7SMichal Kubecek #define EEE_MODES_COUNT \
8b7eeefe7SMichal Kubecek 	(sizeof_field(struct ethtool_eee, supported) * BITS_PER_BYTE)
9b7eeefe7SMichal Kubecek 
10b7eeefe7SMichal Kubecek struct eee_req_info {
11b7eeefe7SMichal Kubecek 	struct ethnl_req_info		base;
12b7eeefe7SMichal Kubecek };
13b7eeefe7SMichal Kubecek 
14b7eeefe7SMichal Kubecek struct eee_reply_data {
15b7eeefe7SMichal Kubecek 	struct ethnl_reply_data		base;
16b7eeefe7SMichal Kubecek 	struct ethtool_eee		eee;
17b7eeefe7SMichal Kubecek };
18b7eeefe7SMichal Kubecek 
19b7eeefe7SMichal Kubecek #define EEE_REPDATA(__reply_base) \
20b7eeefe7SMichal Kubecek 	container_of(__reply_base, struct eee_reply_data, base)
21b7eeefe7SMichal Kubecek 
22ff419afaSJakub Kicinski const struct nla_policy ethnl_eee_get_policy[] = {
23329d9c33SJakub Kicinski 	[ETHTOOL_A_EEE_HEADER]		=
24329d9c33SJakub Kicinski 		NLA_POLICY_NESTED(ethnl_header_policy),
25b7eeefe7SMichal Kubecek };
26b7eeefe7SMichal Kubecek 
eee_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)27b7eeefe7SMichal Kubecek static int eee_prepare_data(const struct ethnl_req_info *req_base,
28b7eeefe7SMichal Kubecek 			    struct ethnl_reply_data *reply_base,
29*f946270dSJakub Kicinski 			    const struct genl_info *info)
30b7eeefe7SMichal Kubecek {
31b7eeefe7SMichal Kubecek 	struct eee_reply_data *data = EEE_REPDATA(reply_base);
32b7eeefe7SMichal Kubecek 	struct net_device *dev = reply_base->dev;
33b7eeefe7SMichal Kubecek 	int ret;
34b7eeefe7SMichal Kubecek 
35b7eeefe7SMichal Kubecek 	if (!dev->ethtool_ops->get_eee)
36b7eeefe7SMichal Kubecek 		return -EOPNOTSUPP;
37b7eeefe7SMichal Kubecek 	ret = ethnl_ops_begin(dev);
38b7eeefe7SMichal Kubecek 	if (ret < 0)
39b7eeefe7SMichal Kubecek 		return ret;
40b7eeefe7SMichal Kubecek 	ret = dev->ethtool_ops->get_eee(dev, &data->eee);
41b7eeefe7SMichal Kubecek 	ethnl_ops_complete(dev);
42b7eeefe7SMichal Kubecek 
43b7eeefe7SMichal Kubecek 	return ret;
44b7eeefe7SMichal Kubecek }
45b7eeefe7SMichal Kubecek 
eee_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)46b7eeefe7SMichal Kubecek static int eee_reply_size(const struct ethnl_req_info *req_base,
47b7eeefe7SMichal Kubecek 			  const struct ethnl_reply_data *reply_base)
48b7eeefe7SMichal Kubecek {
49b7eeefe7SMichal Kubecek 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
50b7eeefe7SMichal Kubecek 	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
51b7eeefe7SMichal Kubecek 	const struct ethtool_eee *eee = &data->eee;
52b7eeefe7SMichal Kubecek 	int len = 0;
53b7eeefe7SMichal Kubecek 	int ret;
54b7eeefe7SMichal Kubecek 
55b7eeefe7SMichal Kubecek 	BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
56b7eeefe7SMichal Kubecek 		     EEE_MODES_COUNT);
57b7eeefe7SMichal Kubecek 	BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
58b7eeefe7SMichal Kubecek 		     EEE_MODES_COUNT);
59b7eeefe7SMichal Kubecek 
60b7eeefe7SMichal Kubecek 	/* MODES_OURS */
61b7eeefe7SMichal Kubecek 	ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
62b7eeefe7SMichal Kubecek 				  EEE_MODES_COUNT, link_mode_names, compact);
63b7eeefe7SMichal Kubecek 	if (ret < 0)
64b7eeefe7SMichal Kubecek 		return ret;
65b7eeefe7SMichal Kubecek 	len += ret;
66b7eeefe7SMichal Kubecek 	/* MODES_PEERS */
67b7eeefe7SMichal Kubecek 	ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
68b7eeefe7SMichal Kubecek 				  EEE_MODES_COUNT, link_mode_names, compact);
69b7eeefe7SMichal Kubecek 	if (ret < 0)
70b7eeefe7SMichal Kubecek 		return ret;
71b7eeefe7SMichal Kubecek 	len += ret;
72b7eeefe7SMichal Kubecek 
73b7eeefe7SMichal Kubecek 	len += nla_total_size(sizeof(u8)) +	/* _EEE_ACTIVE */
74b7eeefe7SMichal Kubecek 	       nla_total_size(sizeof(u8)) +	/* _EEE_ENABLED */
75b7eeefe7SMichal Kubecek 	       nla_total_size(sizeof(u8)) +	/* _EEE_TX_LPI_ENABLED */
76b7eeefe7SMichal Kubecek 	       nla_total_size(sizeof(u32));	/* _EEE_TX_LPI_TIMER */
77b7eeefe7SMichal Kubecek 
78b7eeefe7SMichal Kubecek 	return len;
79b7eeefe7SMichal Kubecek }
80b7eeefe7SMichal Kubecek 
eee_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)81b7eeefe7SMichal Kubecek static int eee_fill_reply(struct sk_buff *skb,
82b7eeefe7SMichal Kubecek 			  const struct ethnl_req_info *req_base,
83b7eeefe7SMichal Kubecek 			  const struct ethnl_reply_data *reply_base)
84b7eeefe7SMichal Kubecek {
85b7eeefe7SMichal Kubecek 	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
86b7eeefe7SMichal Kubecek 	const struct eee_reply_data *data = EEE_REPDATA(reply_base);
87b7eeefe7SMichal Kubecek 	const struct ethtool_eee *eee = &data->eee;
88b7eeefe7SMichal Kubecek 	int ret;
89b7eeefe7SMichal Kubecek 
90b7eeefe7SMichal Kubecek 	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
91b7eeefe7SMichal Kubecek 				 &eee->advertised, &eee->supported,
92b7eeefe7SMichal Kubecek 				 EEE_MODES_COUNT, link_mode_names, compact);
93b7eeefe7SMichal Kubecek 	if (ret < 0)
94b7eeefe7SMichal Kubecek 		return ret;
95b7eeefe7SMichal Kubecek 	ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
96b7eeefe7SMichal Kubecek 				 &eee->lp_advertised, NULL, EEE_MODES_COUNT,
97b7eeefe7SMichal Kubecek 				 link_mode_names, compact);
98b7eeefe7SMichal Kubecek 	if (ret < 0)
99b7eeefe7SMichal Kubecek 		return ret;
100b7eeefe7SMichal Kubecek 
101b7eeefe7SMichal Kubecek 	if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
102b7eeefe7SMichal Kubecek 	    nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
103b7eeefe7SMichal Kubecek 	    nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
104b7eeefe7SMichal Kubecek 		       !!eee->tx_lpi_enabled) ||
105b7eeefe7SMichal Kubecek 	    nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
106b7eeefe7SMichal Kubecek 		return -EMSGSIZE;
107b7eeefe7SMichal Kubecek 
108b7eeefe7SMichal Kubecek 	return 0;
109b7eeefe7SMichal Kubecek }
110b7eeefe7SMichal Kubecek 
111fd77be7bSMichal Kubecek /* EEE_SET */
112fd77be7bSMichal Kubecek 
113ff419afaSJakub Kicinski const struct nla_policy ethnl_eee_set_policy[] = {
114329d9c33SJakub Kicinski 	[ETHTOOL_A_EEE_HEADER]		=
115329d9c33SJakub Kicinski 		NLA_POLICY_NESTED(ethnl_header_policy),
116fd77be7bSMichal Kubecek 	[ETHTOOL_A_EEE_MODES_OURS]	= { .type = NLA_NESTED },
117fd77be7bSMichal Kubecek 	[ETHTOOL_A_EEE_ENABLED]		= { .type = NLA_U8 },
118fd77be7bSMichal Kubecek 	[ETHTOOL_A_EEE_TX_LPI_ENABLED]	= { .type = NLA_U8 },
119fd77be7bSMichal Kubecek 	[ETHTOOL_A_EEE_TX_LPI_TIMER]	= { .type = NLA_U32 },
120fd77be7bSMichal Kubecek };
121fd77be7bSMichal Kubecek 
12204007961SJakub Kicinski static int
ethnl_set_eee_validate(struct ethnl_req_info * req_info,struct genl_info * info)12304007961SJakub Kicinski ethnl_set_eee_validate(struct ethnl_req_info *req_info, struct genl_info *info)
124fd77be7bSMichal Kubecek {
12504007961SJakub Kicinski 	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
12604007961SJakub Kicinski 
12704007961SJakub Kicinski 	return ops->get_eee && ops->set_eee ? 1 : -EOPNOTSUPP;
12804007961SJakub Kicinski }
12904007961SJakub Kicinski 
13004007961SJakub Kicinski static int
ethnl_set_eee(struct ethnl_req_info * req_info,struct genl_info * info)13104007961SJakub Kicinski ethnl_set_eee(struct ethnl_req_info *req_info, struct genl_info *info)
13204007961SJakub Kicinski {
13304007961SJakub Kicinski 	struct net_device *dev = req_info->dev;
1345028588bSJakub Kicinski 	struct nlattr **tb = info->attrs;
1355028588bSJakub Kicinski 	struct ethtool_eee eee = {};
136fd77be7bSMichal Kubecek 	bool mod = false;
137fd77be7bSMichal Kubecek 	int ret;
138fd77be7bSMichal Kubecek 
13904007961SJakub Kicinski 	ret = dev->ethtool_ops->get_eee(dev, &eee);
140fd77be7bSMichal Kubecek 	if (ret < 0)
141fd77be7bSMichal Kubecek 		return ret;
142fd77be7bSMichal Kubecek 
143fd77be7bSMichal Kubecek 	ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
144fd77be7bSMichal Kubecek 				    tb[ETHTOOL_A_EEE_MODES_OURS],
145fd77be7bSMichal Kubecek 				    link_mode_names, info->extack, &mod);
146fd77be7bSMichal Kubecek 	if (ret < 0)
14704007961SJakub Kicinski 		return ret;
148fd77be7bSMichal Kubecek 	ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
149fd77be7bSMichal Kubecek 	ethnl_update_bool32(&eee.tx_lpi_enabled,
150fd77be7bSMichal Kubecek 			    tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
15163cf3238SWong Vee Khee 	ethnl_update_u32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
152fd77be7bSMichal Kubecek 			 &mod);
153fd77be7bSMichal Kubecek 	if (!mod)
15404007961SJakub Kicinski 		return 0;
155fd77be7bSMichal Kubecek 
156fd77be7bSMichal Kubecek 	ret = dev->ethtool_ops->set_eee(dev, &eee);
15704007961SJakub Kicinski 	return ret < 0 ? ret : 1;
158fd77be7bSMichal Kubecek }
15904007961SJakub Kicinski 
16004007961SJakub Kicinski const struct ethnl_request_ops ethnl_eee_request_ops = {
16104007961SJakub Kicinski 	.request_cmd		= ETHTOOL_MSG_EEE_GET,
16204007961SJakub Kicinski 	.reply_cmd		= ETHTOOL_MSG_EEE_GET_REPLY,
16304007961SJakub Kicinski 	.hdr_attr		= ETHTOOL_A_EEE_HEADER,
16404007961SJakub Kicinski 	.req_info_size		= sizeof(struct eee_req_info),
16504007961SJakub Kicinski 	.reply_data_size	= sizeof(struct eee_reply_data),
16604007961SJakub Kicinski 
16704007961SJakub Kicinski 	.prepare_data		= eee_prepare_data,
16804007961SJakub Kicinski 	.reply_size		= eee_reply_size,
16904007961SJakub Kicinski 	.fill_reply		= eee_fill_reply,
17004007961SJakub Kicinski 
17104007961SJakub Kicinski 	.set_validate		= ethnl_set_eee_validate,
17204007961SJakub Kicinski 	.set			= ethnl_set_eee,
17304007961SJakub Kicinski 	.set_ntf_cmd		= ETHTOOL_MSG_EEE_NTF,
17404007961SJakub Kicinski };
175