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