netfilter: arp_tables: restore a SPDX identifier
[linux-2.6-microblaze.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 #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 static const struct nla_policy
23 eee_get_policy[ETHTOOL_A_EEE_MAX + 1] = {
24         [ETHTOOL_A_EEE_UNSPEC]          = { .type = NLA_REJECT },
25         [ETHTOOL_A_EEE_HEADER]          = { .type = NLA_NESTED },
26         [ETHTOOL_A_EEE_MODES_OURS]      = { .type = NLA_REJECT },
27         [ETHTOOL_A_EEE_MODES_PEER]      = { .type = NLA_REJECT },
28         [ETHTOOL_A_EEE_ACTIVE]          = { .type = NLA_REJECT },
29         [ETHTOOL_A_EEE_ENABLED]         = { .type = NLA_REJECT },
30         [ETHTOOL_A_EEE_TX_LPI_ENABLED]  = { .type = NLA_REJECT },
31         [ETHTOOL_A_EEE_TX_LPI_TIMER]    = { .type = NLA_REJECT },
32 };
33
34 static int eee_prepare_data(const struct ethnl_req_info *req_base,
35                             struct ethnl_reply_data *reply_base,
36                             struct genl_info *info)
37 {
38         struct eee_reply_data *data = EEE_REPDATA(reply_base);
39         struct net_device *dev = reply_base->dev;
40         int ret;
41
42         if (!dev->ethtool_ops->get_eee)
43                 return -EOPNOTSUPP;
44         ret = ethnl_ops_begin(dev);
45         if (ret < 0)
46                 return ret;
47         ret = dev->ethtool_ops->get_eee(dev, &data->eee);
48         ethnl_ops_complete(dev);
49
50         return ret;
51 }
52
53 static int eee_reply_size(const struct ethnl_req_info *req_base,
54                           const struct ethnl_reply_data *reply_base)
55 {
56         bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
57         const struct eee_reply_data *data = EEE_REPDATA(reply_base);
58         const struct ethtool_eee *eee = &data->eee;
59         int len = 0;
60         int ret;
61
62         BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
63                      EEE_MODES_COUNT);
64         BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
65                      EEE_MODES_COUNT);
66
67         /* MODES_OURS */
68         ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
69                                   EEE_MODES_COUNT, link_mode_names, compact);
70         if (ret < 0)
71                 return ret;
72         len += ret;
73         /* MODES_PEERS */
74         ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
75                                   EEE_MODES_COUNT, link_mode_names, compact);
76         if (ret < 0)
77                 return ret;
78         len += ret;
79
80         len += nla_total_size(sizeof(u8)) +     /* _EEE_ACTIVE */
81                nla_total_size(sizeof(u8)) +     /* _EEE_ENABLED */
82                nla_total_size(sizeof(u8)) +     /* _EEE_TX_LPI_ENABLED */
83                nla_total_size(sizeof(u32));     /* _EEE_TX_LPI_TIMER */
84
85         return len;
86 }
87
88 static int eee_fill_reply(struct sk_buff *skb,
89                           const struct ethnl_req_info *req_base,
90                           const struct ethnl_reply_data *reply_base)
91 {
92         bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
93         const struct eee_reply_data *data = EEE_REPDATA(reply_base);
94         const struct ethtool_eee *eee = &data->eee;
95         int ret;
96
97         ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
98                                  &eee->advertised, &eee->supported,
99                                  EEE_MODES_COUNT, link_mode_names, compact);
100         if (ret < 0)
101                 return ret;
102         ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
103                                  &eee->lp_advertised, NULL, EEE_MODES_COUNT,
104                                  link_mode_names, compact);
105         if (ret < 0)
106                 return ret;
107
108         if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
109             nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
110             nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
111                        !!eee->tx_lpi_enabled) ||
112             nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
113                 return -EMSGSIZE;
114
115         return 0;
116 }
117
118 const struct ethnl_request_ops ethnl_eee_request_ops = {
119         .request_cmd            = ETHTOOL_MSG_EEE_GET,
120         .reply_cmd              = ETHTOOL_MSG_EEE_GET_REPLY,
121         .hdr_attr               = ETHTOOL_A_EEE_HEADER,
122         .max_attr               = ETHTOOL_A_EEE_MAX,
123         .req_info_size          = sizeof(struct eee_req_info),
124         .reply_data_size        = sizeof(struct eee_reply_data),
125         .request_policy         = eee_get_policy,
126
127         .prepare_data           = eee_prepare_data,
128         .reply_size             = eee_reply_size,
129         .fill_reply             = eee_fill_reply,
130 };
131
132 /* EEE_SET */
133
134 static const struct nla_policy
135 eee_set_policy[ETHTOOL_A_EEE_MAX + 1] = {
136         [ETHTOOL_A_EEE_UNSPEC]          = { .type = NLA_REJECT },
137         [ETHTOOL_A_EEE_HEADER]          = { .type = NLA_NESTED },
138         [ETHTOOL_A_EEE_MODES_OURS]      = { .type = NLA_NESTED },
139         [ETHTOOL_A_EEE_MODES_PEER]      = { .type = NLA_REJECT },
140         [ETHTOOL_A_EEE_ACTIVE]          = { .type = NLA_REJECT },
141         [ETHTOOL_A_EEE_ENABLED]         = { .type = NLA_U8 },
142         [ETHTOOL_A_EEE_TX_LPI_ENABLED]  = { .type = NLA_U8 },
143         [ETHTOOL_A_EEE_TX_LPI_TIMER]    = { .type = NLA_U32 },
144 };
145
146 int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info)
147 {
148         struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1];
149         struct ethtool_eee eee = {};
150         struct ethnl_req_info req_info = {};
151         const struct ethtool_ops *ops;
152         struct net_device *dev;
153         bool mod = false;
154         int ret;
155
156         ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ETHTOOL_A_EEE_MAX,
157                           eee_set_policy, info->extack);
158         if (ret < 0)
159                 return ret;
160         ret = ethnl_parse_header_dev_get(&req_info,
161                                          tb[ETHTOOL_A_EEE_HEADER],
162                                          genl_info_net(info), info->extack,
163                                          true);
164         if (ret < 0)
165                 return ret;
166         dev = req_info.dev;
167         ops = dev->ethtool_ops;
168         ret = -EOPNOTSUPP;
169         if (!ops->get_eee || !ops->set_eee)
170                 goto out_dev;
171
172         rtnl_lock();
173         ret = ethnl_ops_begin(dev);
174         if (ret < 0)
175                 goto out_rtnl;
176         ret = ops->get_eee(dev, &eee);
177         if (ret < 0)
178                 goto out_ops;
179
180         ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
181                                     tb[ETHTOOL_A_EEE_MODES_OURS],
182                                     link_mode_names, info->extack, &mod);
183         if (ret < 0)
184                 goto out_ops;
185         ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
186         ethnl_update_bool32(&eee.tx_lpi_enabled,
187                             tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
188         ethnl_update_bool32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
189                             &mod);
190         ret = 0;
191         if (!mod)
192                 goto out_ops;
193
194         ret = dev->ethtool_ops->set_eee(dev, &eee);
195         if (ret < 0)
196                 goto out_ops;
197         ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL);
198
199 out_ops:
200         ethnl_ops_complete(dev);
201 out_rtnl:
202         rtnl_unlock();
203 out_dev:
204         dev_put(dev);
205         return ret;
206 }