Merge tag 'microblaze-v6.6' of git://git.monstr.eu/linux-2.6-microblaze
[platform/kernel/linux-starfive.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 const struct nla_policy ethnl_eee_get_policy[] = {
23         [ETHTOOL_A_EEE_HEADER]          =
24                 NLA_POLICY_NESTED(ethnl_header_policy),
25 };
26
27 static int eee_prepare_data(const struct ethnl_req_info *req_base,
28                             struct ethnl_reply_data *reply_base,
29                             const struct genl_info *info)
30 {
31         struct eee_reply_data *data = EEE_REPDATA(reply_base);
32         struct net_device *dev = reply_base->dev;
33         int ret;
34
35         if (!dev->ethtool_ops->get_eee)
36                 return -EOPNOTSUPP;
37         ret = ethnl_ops_begin(dev);
38         if (ret < 0)
39                 return ret;
40         ret = dev->ethtool_ops->get_eee(dev, &data->eee);
41         ethnl_ops_complete(dev);
42
43         return ret;
44 }
45
46 static int eee_reply_size(const struct ethnl_req_info *req_base,
47                           const struct ethnl_reply_data *reply_base)
48 {
49         bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
50         const struct eee_reply_data *data = EEE_REPDATA(reply_base);
51         const struct ethtool_eee *eee = &data->eee;
52         int len = 0;
53         int ret;
54
55         BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE !=
56                      EEE_MODES_COUNT);
57         BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE !=
58                      EEE_MODES_COUNT);
59
60         /* MODES_OURS */
61         ret = ethnl_bitset32_size(&eee->advertised, &eee->supported,
62                                   EEE_MODES_COUNT, link_mode_names, compact);
63         if (ret < 0)
64                 return ret;
65         len += ret;
66         /* MODES_PEERS */
67         ret = ethnl_bitset32_size(&eee->lp_advertised, NULL,
68                                   EEE_MODES_COUNT, link_mode_names, compact);
69         if (ret < 0)
70                 return ret;
71         len += ret;
72
73         len += nla_total_size(sizeof(u8)) +     /* _EEE_ACTIVE */
74                nla_total_size(sizeof(u8)) +     /* _EEE_ENABLED */
75                nla_total_size(sizeof(u8)) +     /* _EEE_TX_LPI_ENABLED */
76                nla_total_size(sizeof(u32));     /* _EEE_TX_LPI_TIMER */
77
78         return len;
79 }
80
81 static int eee_fill_reply(struct sk_buff *skb,
82                           const struct ethnl_req_info *req_base,
83                           const struct ethnl_reply_data *reply_base)
84 {
85         bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
86         const struct eee_reply_data *data = EEE_REPDATA(reply_base);
87         const struct ethtool_eee *eee = &data->eee;
88         int ret;
89
90         ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS,
91                                  &eee->advertised, &eee->supported,
92                                  EEE_MODES_COUNT, link_mode_names, compact);
93         if (ret < 0)
94                 return ret;
95         ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER,
96                                  &eee->lp_advertised, NULL, EEE_MODES_COUNT,
97                                  link_mode_names, compact);
98         if (ret < 0)
99                 return ret;
100
101         if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) ||
102             nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) ||
103             nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED,
104                        !!eee->tx_lpi_enabled) ||
105             nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer))
106                 return -EMSGSIZE;
107
108         return 0;
109 }
110
111 /* EEE_SET */
112
113 const struct nla_policy ethnl_eee_set_policy[] = {
114         [ETHTOOL_A_EEE_HEADER]          =
115                 NLA_POLICY_NESTED(ethnl_header_policy),
116         [ETHTOOL_A_EEE_MODES_OURS]      = { .type = NLA_NESTED },
117         [ETHTOOL_A_EEE_ENABLED]         = { .type = NLA_U8 },
118         [ETHTOOL_A_EEE_TX_LPI_ENABLED]  = { .type = NLA_U8 },
119         [ETHTOOL_A_EEE_TX_LPI_TIMER]    = { .type = NLA_U32 },
120 };
121
122 static int
123 ethnl_set_eee_validate(struct ethnl_req_info *req_info, struct genl_info *info)
124 {
125         const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
126
127         return ops->get_eee && ops->set_eee ? 1 : -EOPNOTSUPP;
128 }
129
130 static int
131 ethnl_set_eee(struct ethnl_req_info *req_info, struct genl_info *info)
132 {
133         struct net_device *dev = req_info->dev;
134         struct nlattr **tb = info->attrs;
135         struct ethtool_eee eee = {};
136         bool mod = false;
137         int ret;
138
139         ret = dev->ethtool_ops->get_eee(dev, &eee);
140         if (ret < 0)
141                 return ret;
142
143         ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT,
144                                     tb[ETHTOOL_A_EEE_MODES_OURS],
145                                     link_mode_names, info->extack, &mod);
146         if (ret < 0)
147                 return ret;
148         ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod);
149         ethnl_update_bool32(&eee.tx_lpi_enabled,
150                             tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod);
151         ethnl_update_u32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER],
152                          &mod);
153         if (!mod)
154                 return 0;
155
156         ret = dev->ethtool_ops->set_eee(dev, &eee);
157         return ret < 0 ? ret : 1;
158 }
159
160 const struct ethnl_request_ops ethnl_eee_request_ops = {
161         .request_cmd            = ETHTOOL_MSG_EEE_GET,
162         .reply_cmd              = ETHTOOL_MSG_EEE_GET_REPLY,
163         .hdr_attr               = ETHTOOL_A_EEE_HEADER,
164         .req_info_size          = sizeof(struct eee_req_info),
165         .reply_data_size        = sizeof(struct eee_reply_data),
166
167         .prepare_data           = eee_prepare_data,
168         .reply_size             = eee_reply_size,
169         .fill_reply             = eee_fill_reply,
170
171         .set_validate           = ethnl_set_eee_validate,
172         .set                    = ethnl_set_eee,
173         .set_ntf_cmd            = ETHTOOL_MSG_EEE_NTF,
174 };