GNU Linux-libre 6.9.1-gnu
[releases.git] / net / ethtool / eee.c
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 };