1f625aa9bSMichal Kubecek // SPDX-License-Identifier: GPL-2.0-only
2f625aa9bSMichal Kubecek
3f625aa9bSMichal Kubecek #include "netlink.h"
4f625aa9bSMichal Kubecek #include "common.h"
5f625aa9bSMichal Kubecek #include "bitset.h"
6f625aa9bSMichal Kubecek
7c8907043SDanielle Ratson /* LINKMODES_GET */
8c8907043SDanielle Ratson
9f625aa9bSMichal Kubecek struct linkmodes_req_info {
10f625aa9bSMichal Kubecek struct ethnl_req_info base;
11f625aa9bSMichal Kubecek };
12f625aa9bSMichal Kubecek
13f625aa9bSMichal Kubecek struct linkmodes_reply_data {
14f625aa9bSMichal Kubecek struct ethnl_reply_data base;
15f625aa9bSMichal Kubecek struct ethtool_link_ksettings ksettings;
16f625aa9bSMichal Kubecek struct ethtool_link_settings *lsettings;
17f625aa9bSMichal Kubecek bool peer_empty;
18f625aa9bSMichal Kubecek };
19f625aa9bSMichal Kubecek
20f625aa9bSMichal Kubecek #define LINKMODES_REPDATA(__reply_base) \
21f625aa9bSMichal Kubecek container_of(__reply_base, struct linkmodes_reply_data, base)
22f625aa9bSMichal Kubecek
23ff419afaSJakub Kicinski const struct nla_policy ethnl_linkmodes_get_policy[] = {
24329d9c33SJakub Kicinski [ETHTOOL_A_LINKMODES_HEADER] =
25329d9c33SJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy),
26f625aa9bSMichal Kubecek };
27f625aa9bSMichal Kubecek
linkmodes_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)28f625aa9bSMichal Kubecek static int linkmodes_prepare_data(const struct ethnl_req_info *req_base,
29f625aa9bSMichal Kubecek struct ethnl_reply_data *reply_base,
30*f946270dSJakub Kicinski const struct genl_info *info)
31f625aa9bSMichal Kubecek {
32f625aa9bSMichal Kubecek struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
33f625aa9bSMichal Kubecek struct net_device *dev = reply_base->dev;
34f625aa9bSMichal Kubecek int ret;
35f625aa9bSMichal Kubecek
36f625aa9bSMichal Kubecek data->lsettings = &data->ksettings.base;
37f625aa9bSMichal Kubecek
38f625aa9bSMichal Kubecek ret = ethnl_ops_begin(dev);
39f625aa9bSMichal Kubecek if (ret < 0)
40f625aa9bSMichal Kubecek return ret;
41f625aa9bSMichal Kubecek
42f625aa9bSMichal Kubecek ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
43f625aa9bSMichal Kubecek if (ret < 0 && info) {
44f625aa9bSMichal Kubecek GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
45f625aa9bSMichal Kubecek goto out;
46f625aa9bSMichal Kubecek }
47f625aa9bSMichal Kubecek
487dc33f09SDanielle Ratson if (!dev->ethtool_ops->cap_link_lanes_supported)
497dc33f09SDanielle Ratson data->ksettings.lanes = 0;
507dc33f09SDanielle Ratson
51f625aa9bSMichal Kubecek data->peer_empty =
52f625aa9bSMichal Kubecek bitmap_empty(data->ksettings.link_modes.lp_advertising,
53f625aa9bSMichal Kubecek __ETHTOOL_LINK_MODE_MASK_NBITS);
54f625aa9bSMichal Kubecek
55f625aa9bSMichal Kubecek out:
56f625aa9bSMichal Kubecek ethnl_ops_complete(dev);
57f625aa9bSMichal Kubecek return ret;
58f625aa9bSMichal Kubecek }
59f625aa9bSMichal Kubecek
linkmodes_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)60f625aa9bSMichal Kubecek static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
61f625aa9bSMichal Kubecek const struct ethnl_reply_data *reply_base)
62f625aa9bSMichal Kubecek {
63f625aa9bSMichal Kubecek const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
64f625aa9bSMichal Kubecek const struct ethtool_link_ksettings *ksettings = &data->ksettings;
65bdbdac76SOleksij Rempel const struct ethtool_link_settings *lsettings = &ksettings->base;
66f625aa9bSMichal Kubecek bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
67f625aa9bSMichal Kubecek int len, ret;
68f625aa9bSMichal Kubecek
69f625aa9bSMichal Kubecek len = nla_total_size(sizeof(u8)) /* LINKMODES_AUTONEG */
70f625aa9bSMichal Kubecek + nla_total_size(sizeof(u32)) /* LINKMODES_SPEED */
717dc33f09SDanielle Ratson + nla_total_size(sizeof(u32)) /* LINKMODES_LANES */
72f625aa9bSMichal Kubecek + nla_total_size(sizeof(u8)) /* LINKMODES_DUPLEX */
730c3e10cbSSean Anderson + nla_total_size(sizeof(u8)) /* LINKMODES_RATE_MATCHING */
74f625aa9bSMichal Kubecek + 0;
75f625aa9bSMichal Kubecek ret = ethnl_bitset_size(ksettings->link_modes.advertising,
76f625aa9bSMichal Kubecek ksettings->link_modes.supported,
77f625aa9bSMichal Kubecek __ETHTOOL_LINK_MODE_MASK_NBITS,
78f625aa9bSMichal Kubecek link_mode_names, compact);
79f625aa9bSMichal Kubecek if (ret < 0)
80f625aa9bSMichal Kubecek return ret;
81f625aa9bSMichal Kubecek len += ret;
82f625aa9bSMichal Kubecek if (!data->peer_empty) {
83f625aa9bSMichal Kubecek ret = ethnl_bitset_size(ksettings->link_modes.lp_advertising,
84f625aa9bSMichal Kubecek NULL, __ETHTOOL_LINK_MODE_MASK_NBITS,
85f625aa9bSMichal Kubecek link_mode_names, compact);
86f625aa9bSMichal Kubecek if (ret < 0)
87f625aa9bSMichal Kubecek return ret;
88f625aa9bSMichal Kubecek len += ret;
89f625aa9bSMichal Kubecek }
90f625aa9bSMichal Kubecek
91bdbdac76SOleksij Rempel if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED)
92bdbdac76SOleksij Rempel len += nla_total_size(sizeof(u8));
93bdbdac76SOleksij Rempel
94bdbdac76SOleksij Rempel if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED)
95bdbdac76SOleksij Rempel len += nla_total_size(sizeof(u8));
96bdbdac76SOleksij Rempel
97f625aa9bSMichal Kubecek return len;
98f625aa9bSMichal Kubecek }
99f625aa9bSMichal Kubecek
linkmodes_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)100f625aa9bSMichal Kubecek static int linkmodes_fill_reply(struct sk_buff *skb,
101f625aa9bSMichal Kubecek const struct ethnl_req_info *req_base,
102f625aa9bSMichal Kubecek const struct ethnl_reply_data *reply_base)
103f625aa9bSMichal Kubecek {
104f625aa9bSMichal Kubecek const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
105f625aa9bSMichal Kubecek const struct ethtool_link_ksettings *ksettings = &data->ksettings;
106f625aa9bSMichal Kubecek const struct ethtool_link_settings *lsettings = &ksettings->base;
107f625aa9bSMichal Kubecek bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
108f625aa9bSMichal Kubecek int ret;
109f625aa9bSMichal Kubecek
110f625aa9bSMichal Kubecek if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg))
111f625aa9bSMichal Kubecek return -EMSGSIZE;
112f625aa9bSMichal Kubecek
113f625aa9bSMichal Kubecek ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_OURS,
114f625aa9bSMichal Kubecek ksettings->link_modes.advertising,
115f625aa9bSMichal Kubecek ksettings->link_modes.supported,
116f625aa9bSMichal Kubecek __ETHTOOL_LINK_MODE_MASK_NBITS, link_mode_names,
117f625aa9bSMichal Kubecek compact);
118f625aa9bSMichal Kubecek if (ret < 0)
119f625aa9bSMichal Kubecek return -EMSGSIZE;
120f625aa9bSMichal Kubecek if (!data->peer_empty) {
121f625aa9bSMichal Kubecek ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_PEER,
122f625aa9bSMichal Kubecek ksettings->link_modes.lp_advertising,
123f625aa9bSMichal Kubecek NULL, __ETHTOOL_LINK_MODE_MASK_NBITS,
124f625aa9bSMichal Kubecek link_mode_names, compact);
125f625aa9bSMichal Kubecek if (ret < 0)
126f625aa9bSMichal Kubecek return -EMSGSIZE;
127f625aa9bSMichal Kubecek }
128f625aa9bSMichal Kubecek
129f625aa9bSMichal Kubecek if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) ||
130f625aa9bSMichal Kubecek nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex))
131f625aa9bSMichal Kubecek return -EMSGSIZE;
132f625aa9bSMichal Kubecek
1337dc33f09SDanielle Ratson if (ksettings->lanes &&
1347dc33f09SDanielle Ratson nla_put_u32(skb, ETHTOOL_A_LINKMODES_LANES, ksettings->lanes))
1357dc33f09SDanielle Ratson return -EMSGSIZE;
1367dc33f09SDanielle Ratson
137bdbdac76SOleksij Rempel if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED &&
138bdbdac76SOleksij Rempel nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
139bdbdac76SOleksij Rempel lsettings->master_slave_cfg))
140bdbdac76SOleksij Rempel return -EMSGSIZE;
141bdbdac76SOleksij Rempel
142bdbdac76SOleksij Rempel if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED &&
143bdbdac76SOleksij Rempel nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE,
144bdbdac76SOleksij Rempel lsettings->master_slave_state))
145bdbdac76SOleksij Rempel return -EMSGSIZE;
146bdbdac76SOleksij Rempel
1470c3e10cbSSean Anderson if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_RATE_MATCHING,
1480c3e10cbSSean Anderson lsettings->rate_matching))
1490c3e10cbSSean Anderson return -EMSGSIZE;
1500c3e10cbSSean Anderson
151f625aa9bSMichal Kubecek return 0;
152f625aa9bSMichal Kubecek }
153f625aa9bSMichal Kubecek
154bfbcfe20SMichal Kubecek /* LINKMODES_SET */
155bfbcfe20SMichal Kubecek
156ff419afaSJakub Kicinski const struct nla_policy ethnl_linkmodes_set_policy[] = {
157329d9c33SJakub Kicinski [ETHTOOL_A_LINKMODES_HEADER] =
158329d9c33SJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy),
159bfbcfe20SMichal Kubecek [ETHTOOL_A_LINKMODES_AUTONEG] = { .type = NLA_U8 },
160bfbcfe20SMichal Kubecek [ETHTOOL_A_LINKMODES_OURS] = { .type = NLA_NESTED },
161bfbcfe20SMichal Kubecek [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_U32 },
162bfbcfe20SMichal Kubecek [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_U8 },
163bdbdac76SOleksij Rempel [ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG] = { .type = NLA_U8 },
164012ce4ddSDanielle Ratson [ETHTOOL_A_LINKMODES_LANES] = NLA_POLICY_RANGE(NLA_U32, 1, 8),
165bfbcfe20SMichal Kubecek };
166bfbcfe20SMichal Kubecek
167012ce4ddSDanielle Ratson /* Set advertised link modes to all supported modes matching requested speed,
168012ce4ddSDanielle Ratson * lanes and duplex values. Called when autonegotiation is on, speed, lanes or
169012ce4ddSDanielle Ratson * duplex is requested but no link mode change. This is done in userspace with
170012ce4ddSDanielle Ratson * ioctl() interface, move it into kernel for netlink.
171bfbcfe20SMichal Kubecek * Returns true if advertised modes bitmap was modified.
172bfbcfe20SMichal Kubecek */
ethnl_auto_linkmodes(struct ethtool_link_ksettings * ksettings,bool req_speed,bool req_lanes,bool req_duplex)173bfbcfe20SMichal Kubecek static bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings,
174012ce4ddSDanielle Ratson bool req_speed, bool req_lanes, bool req_duplex)
175bfbcfe20SMichal Kubecek {
176bfbcfe20SMichal Kubecek unsigned long *advertising = ksettings->link_modes.advertising;
177bfbcfe20SMichal Kubecek unsigned long *supported = ksettings->link_modes.supported;
178bfbcfe20SMichal Kubecek DECLARE_BITMAP(old_adv, __ETHTOOL_LINK_MODE_MASK_NBITS);
179bfbcfe20SMichal Kubecek unsigned int i;
180bfbcfe20SMichal Kubecek
181bfbcfe20SMichal Kubecek bitmap_copy(old_adv, advertising, __ETHTOOL_LINK_MODE_MASK_NBITS);
182bfbcfe20SMichal Kubecek
183bfbcfe20SMichal Kubecek for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) {
184bfbcfe20SMichal Kubecek const struct link_mode_info *info = &link_mode_params[i];
185bfbcfe20SMichal Kubecek
186bfbcfe20SMichal Kubecek if (info->speed == SPEED_UNKNOWN)
187bfbcfe20SMichal Kubecek continue;
188bfbcfe20SMichal Kubecek if (test_bit(i, supported) &&
189bfbcfe20SMichal Kubecek (!req_speed || info->speed == ksettings->base.speed) &&
190012ce4ddSDanielle Ratson (!req_lanes || info->lanes == ksettings->lanes) &&
191bfbcfe20SMichal Kubecek (!req_duplex || info->duplex == ksettings->base.duplex))
192bfbcfe20SMichal Kubecek set_bit(i, advertising);
193bfbcfe20SMichal Kubecek else
194bfbcfe20SMichal Kubecek clear_bit(i, advertising);
195bfbcfe20SMichal Kubecek }
196bfbcfe20SMichal Kubecek
197bfbcfe20SMichal Kubecek return !bitmap_equal(old_adv, advertising,
198bfbcfe20SMichal Kubecek __ETHTOOL_LINK_MODE_MASK_NBITS);
199bfbcfe20SMichal Kubecek }
200bfbcfe20SMichal Kubecek
ethnl_validate_master_slave_cfg(u8 cfg)201bdbdac76SOleksij Rempel static bool ethnl_validate_master_slave_cfg(u8 cfg)
202bdbdac76SOleksij Rempel {
203bdbdac76SOleksij Rempel switch (cfg) {
204bdbdac76SOleksij Rempel case MASTER_SLAVE_CFG_MASTER_PREFERRED:
205bdbdac76SOleksij Rempel case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
206bdbdac76SOleksij Rempel case MASTER_SLAVE_CFG_MASTER_FORCE:
207bdbdac76SOleksij Rempel case MASTER_SLAVE_CFG_SLAVE_FORCE:
208bdbdac76SOleksij Rempel return true;
209bdbdac76SOleksij Rempel }
210bdbdac76SOleksij Rempel
211bdbdac76SOleksij Rempel return false;
212bdbdac76SOleksij Rempel }
213bdbdac76SOleksij Rempel
ethnl_check_linkmodes(struct genl_info * info,struct nlattr ** tb)214189e7a8dSDanielle Ratson static int ethnl_check_linkmodes(struct genl_info *info, struct nlattr **tb)
215189e7a8dSDanielle Ratson {
216012ce4ddSDanielle Ratson const struct nlattr *master_slave_cfg, *lanes_cfg;
217189e7a8dSDanielle Ratson
218189e7a8dSDanielle Ratson master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG];
219189e7a8dSDanielle Ratson if (master_slave_cfg &&
220189e7a8dSDanielle Ratson !ethnl_validate_master_slave_cfg(nla_get_u8(master_slave_cfg))) {
221189e7a8dSDanielle Ratson NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
222189e7a8dSDanielle Ratson "master/slave value is invalid");
223189e7a8dSDanielle Ratson return -EOPNOTSUPP;
224189e7a8dSDanielle Ratson }
225189e7a8dSDanielle Ratson
226012ce4ddSDanielle Ratson lanes_cfg = tb[ETHTOOL_A_LINKMODES_LANES];
227012ce4ddSDanielle Ratson if (lanes_cfg && !is_power_of_2(nla_get_u32(lanes_cfg))) {
228012ce4ddSDanielle Ratson NL_SET_ERR_MSG_ATTR(info->extack, lanes_cfg,
229012ce4ddSDanielle Ratson "lanes value is invalid");
230012ce4ddSDanielle Ratson return -EINVAL;
231012ce4ddSDanielle Ratson }
232012ce4ddSDanielle Ratson
233189e7a8dSDanielle Ratson return 0;
234189e7a8dSDanielle Ratson }
235189e7a8dSDanielle Ratson
ethnl_update_linkmodes(struct genl_info * info,struct nlattr ** tb,struct ethtool_link_ksettings * ksettings,bool * mod,const struct net_device * dev)236bfbcfe20SMichal Kubecek static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb,
237bfbcfe20SMichal Kubecek struct ethtool_link_ksettings *ksettings,
238012ce4ddSDanielle Ratson bool *mod, const struct net_device *dev)
239bfbcfe20SMichal Kubecek {
240bfbcfe20SMichal Kubecek struct ethtool_link_settings *lsettings = &ksettings->base;
241012ce4ddSDanielle Ratson bool req_speed, req_lanes, req_duplex;
242012ce4ddSDanielle Ratson const struct nlattr *master_slave_cfg, *lanes_cfg;
243bfbcfe20SMichal Kubecek int ret;
244bfbcfe20SMichal Kubecek
245bdbdac76SOleksij Rempel master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG];
246bdbdac76SOleksij Rempel if (master_slave_cfg) {
247bdbdac76SOleksij Rempel if (lsettings->master_slave_cfg == MASTER_SLAVE_CFG_UNSUPPORTED) {
248bdbdac76SOleksij Rempel NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
249bdbdac76SOleksij Rempel "master/slave configuration not supported by device");
250bdbdac76SOleksij Rempel return -EOPNOTSUPP;
251bdbdac76SOleksij Rempel }
252bdbdac76SOleksij Rempel }
253bdbdac76SOleksij Rempel
254bfbcfe20SMichal Kubecek *mod = false;
255bfbcfe20SMichal Kubecek req_speed = tb[ETHTOOL_A_LINKMODES_SPEED];
256012ce4ddSDanielle Ratson req_lanes = tb[ETHTOOL_A_LINKMODES_LANES];
257bfbcfe20SMichal Kubecek req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX];
258bfbcfe20SMichal Kubecek
259bfbcfe20SMichal Kubecek ethnl_update_u8(&lsettings->autoneg, tb[ETHTOOL_A_LINKMODES_AUTONEG],
260bfbcfe20SMichal Kubecek mod);
261012ce4ddSDanielle Ratson
262012ce4ddSDanielle Ratson lanes_cfg = tb[ETHTOOL_A_LINKMODES_LANES];
263012ce4ddSDanielle Ratson if (lanes_cfg) {
264012ce4ddSDanielle Ratson /* If autoneg is off and lanes parameter is not supported by the
265012ce4ddSDanielle Ratson * driver, return an error.
266012ce4ddSDanielle Ratson */
267012ce4ddSDanielle Ratson if (!lsettings->autoneg &&
268012ce4ddSDanielle Ratson !dev->ethtool_ops->cap_link_lanes_supported) {
269012ce4ddSDanielle Ratson NL_SET_ERR_MSG_ATTR(info->extack, lanes_cfg,
270012ce4ddSDanielle Ratson "lanes configuration not supported by device");
271012ce4ddSDanielle Ratson return -EOPNOTSUPP;
272012ce4ddSDanielle Ratson }
273e847c767SAndy Roulin } else if (!lsettings->autoneg && ksettings->lanes) {
274e847c767SAndy Roulin /* If autoneg is off and lanes parameter is not passed from user but
275e847c767SAndy Roulin * it was defined previously then set the lanes parameter to 0.
276012ce4ddSDanielle Ratson */
277012ce4ddSDanielle Ratson ksettings->lanes = 0;
278e847c767SAndy Roulin *mod = true;
279012ce4ddSDanielle Ratson }
280012ce4ddSDanielle Ratson
281bfbcfe20SMichal Kubecek ret = ethnl_update_bitset(ksettings->link_modes.advertising,
282bfbcfe20SMichal Kubecek __ETHTOOL_LINK_MODE_MASK_NBITS,
283bfbcfe20SMichal Kubecek tb[ETHTOOL_A_LINKMODES_OURS], link_mode_names,
284bfbcfe20SMichal Kubecek info->extack, mod);
285bfbcfe20SMichal Kubecek if (ret < 0)
286bfbcfe20SMichal Kubecek return ret;
287bfbcfe20SMichal Kubecek ethnl_update_u32(&lsettings->speed, tb[ETHTOOL_A_LINKMODES_SPEED],
288bfbcfe20SMichal Kubecek mod);
289012ce4ddSDanielle Ratson ethnl_update_u32(&ksettings->lanes, lanes_cfg, mod);
290bfbcfe20SMichal Kubecek ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX],
291bfbcfe20SMichal Kubecek mod);
292bdbdac76SOleksij Rempel ethnl_update_u8(&lsettings->master_slave_cfg, master_slave_cfg, mod);
293bfbcfe20SMichal Kubecek
294bfbcfe20SMichal Kubecek if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg &&
295012ce4ddSDanielle Ratson (req_speed || req_lanes || req_duplex) &&
296012ce4ddSDanielle Ratson ethnl_auto_linkmodes(ksettings, req_speed, req_lanes, req_duplex))
297bfbcfe20SMichal Kubecek *mod = true;
298bfbcfe20SMichal Kubecek
299bfbcfe20SMichal Kubecek return 0;
300bfbcfe20SMichal Kubecek }
301bfbcfe20SMichal Kubecek
30204007961SJakub Kicinski static int
ethnl_set_linkmodes_validate(struct ethnl_req_info * req_info,struct genl_info * info)30304007961SJakub Kicinski ethnl_set_linkmodes_validate(struct ethnl_req_info *req_info,
30404007961SJakub Kicinski struct genl_info *info)
305bfbcfe20SMichal Kubecek {
30604007961SJakub Kicinski const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
307bfbcfe20SMichal Kubecek int ret;
308bfbcfe20SMichal Kubecek
30904007961SJakub Kicinski ret = ethnl_check_linkmodes(info, info->attrs);
310189e7a8dSDanielle Ratson if (ret < 0)
311189e7a8dSDanielle Ratson return ret;
312189e7a8dSDanielle Ratson
31304007961SJakub Kicinski if (!ops->get_link_ksettings || !ops->set_link_ksettings)
31404007961SJakub Kicinski return -EOPNOTSUPP;
31504007961SJakub Kicinski return 1;
31604007961SJakub Kicinski }
317bfbcfe20SMichal Kubecek
31804007961SJakub Kicinski static int
ethnl_set_linkmodes(struct ethnl_req_info * req_info,struct genl_info * info)31904007961SJakub Kicinski ethnl_set_linkmodes(struct ethnl_req_info *req_info, struct genl_info *info)
32004007961SJakub Kicinski {
32104007961SJakub Kicinski struct ethtool_link_ksettings ksettings = {};
32204007961SJakub Kicinski struct net_device *dev = req_info->dev;
32304007961SJakub Kicinski struct nlattr **tb = info->attrs;
32404007961SJakub Kicinski bool mod = false;
32504007961SJakub Kicinski int ret;
326bfbcfe20SMichal Kubecek
327bfbcfe20SMichal Kubecek ret = __ethtool_get_link_ksettings(dev, &ksettings);
328bfbcfe20SMichal Kubecek if (ret < 0) {
329bfbcfe20SMichal Kubecek GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
33004007961SJakub Kicinski return ret;
331bfbcfe20SMichal Kubecek }
332bfbcfe20SMichal Kubecek
333012ce4ddSDanielle Ratson ret = ethnl_update_linkmodes(info, tb, &ksettings, &mod, dev);
334bfbcfe20SMichal Kubecek if (ret < 0)
33504007961SJakub Kicinski return ret;
33604007961SJakub Kicinski if (!mod)
33704007961SJakub Kicinski return 0;
338bfbcfe20SMichal Kubecek
339bfbcfe20SMichal Kubecek ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
34004007961SJakub Kicinski if (ret < 0) {
341bfbcfe20SMichal Kubecek GENL_SET_ERR_MSG(info, "link settings update failed");
342bfbcfe20SMichal Kubecek return ret;
343bfbcfe20SMichal Kubecek }
34404007961SJakub Kicinski
34504007961SJakub Kicinski return 1;
34604007961SJakub Kicinski }
34704007961SJakub Kicinski
34804007961SJakub Kicinski const struct ethnl_request_ops ethnl_linkmodes_request_ops = {
34904007961SJakub Kicinski .request_cmd = ETHTOOL_MSG_LINKMODES_GET,
35004007961SJakub Kicinski .reply_cmd = ETHTOOL_MSG_LINKMODES_GET_REPLY,
35104007961SJakub Kicinski .hdr_attr = ETHTOOL_A_LINKMODES_HEADER,
35204007961SJakub Kicinski .req_info_size = sizeof(struct linkmodes_req_info),
35304007961SJakub Kicinski .reply_data_size = sizeof(struct linkmodes_reply_data),
35404007961SJakub Kicinski
35504007961SJakub Kicinski .prepare_data = linkmodes_prepare_data,
35604007961SJakub Kicinski .reply_size = linkmodes_reply_size,
35704007961SJakub Kicinski .fill_reply = linkmodes_fill_reply,
35804007961SJakub Kicinski
35904007961SJakub Kicinski .set_validate = ethnl_set_linkmodes_validate,
36004007961SJakub Kicinski .set = ethnl_set_linkmodes,
36104007961SJakub Kicinski .set_ntf_cmd = ETHTOOL_MSG_LINKMODES_NTF,
36204007961SJakub Kicinski };
363