162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include "netlink.h" 462306a36Sopenharmony_ci#include "common.h" 562306a36Sopenharmony_ci#include "bitset.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* LINKMODES_GET */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistruct linkmodes_req_info { 1062306a36Sopenharmony_ci struct ethnl_req_info base; 1162306a36Sopenharmony_ci}; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistruct linkmodes_reply_data { 1462306a36Sopenharmony_ci struct ethnl_reply_data base; 1562306a36Sopenharmony_ci struct ethtool_link_ksettings ksettings; 1662306a36Sopenharmony_ci struct ethtool_link_settings *lsettings; 1762306a36Sopenharmony_ci bool peer_empty; 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define LINKMODES_REPDATA(__reply_base) \ 2162306a36Sopenharmony_ci container_of(__reply_base, struct linkmodes_reply_data, base) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciconst struct nla_policy ethnl_linkmodes_get_policy[] = { 2462306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_HEADER] = 2562306a36Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int linkmodes_prepare_data(const struct ethnl_req_info *req_base, 2962306a36Sopenharmony_ci struct ethnl_reply_data *reply_base, 3062306a36Sopenharmony_ci const struct genl_info *info) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); 3362306a36Sopenharmony_ci struct net_device *dev = reply_base->dev; 3462306a36Sopenharmony_ci int ret; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci data->lsettings = &data->ksettings.base; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ret = ethnl_ops_begin(dev); 3962306a36Sopenharmony_ci if (ret < 0) 4062306a36Sopenharmony_ci return ret; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci ret = __ethtool_get_link_ksettings(dev, &data->ksettings); 4362306a36Sopenharmony_ci if (ret < 0 && info) { 4462306a36Sopenharmony_ci GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 4562306a36Sopenharmony_ci goto out; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (!dev->ethtool_ops->cap_link_lanes_supported) 4962306a36Sopenharmony_ci data->ksettings.lanes = 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci data->peer_empty = 5262306a36Sopenharmony_ci bitmap_empty(data->ksettings.link_modes.lp_advertising, 5362306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciout: 5662306a36Sopenharmony_ci ethnl_ops_complete(dev); 5762306a36Sopenharmony_ci return ret; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int linkmodes_reply_size(const struct ethnl_req_info *req_base, 6162306a36Sopenharmony_ci const struct ethnl_reply_data *reply_base) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); 6462306a36Sopenharmony_ci const struct ethtool_link_ksettings *ksettings = &data->ksettings; 6562306a36Sopenharmony_ci const struct ethtool_link_settings *lsettings = &ksettings->base; 6662306a36Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 6762306a36Sopenharmony_ci int len, ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci len = nla_total_size(sizeof(u8)) /* LINKMODES_AUTONEG */ 7062306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* LINKMODES_SPEED */ 7162306a36Sopenharmony_ci + nla_total_size(sizeof(u32)) /* LINKMODES_LANES */ 7262306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* LINKMODES_DUPLEX */ 7362306a36Sopenharmony_ci + nla_total_size(sizeof(u8)) /* LINKMODES_RATE_MATCHING */ 7462306a36Sopenharmony_ci + 0; 7562306a36Sopenharmony_ci ret = ethnl_bitset_size(ksettings->link_modes.advertising, 7662306a36Sopenharmony_ci ksettings->link_modes.supported, 7762306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 7862306a36Sopenharmony_ci link_mode_names, compact); 7962306a36Sopenharmony_ci if (ret < 0) 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci len += ret; 8262306a36Sopenharmony_ci if (!data->peer_empty) { 8362306a36Sopenharmony_ci ret = ethnl_bitset_size(ksettings->link_modes.lp_advertising, 8462306a36Sopenharmony_ci NULL, __ETHTOOL_LINK_MODE_MASK_NBITS, 8562306a36Sopenharmony_ci link_mode_names, compact); 8662306a36Sopenharmony_ci if (ret < 0) 8762306a36Sopenharmony_ci return ret; 8862306a36Sopenharmony_ci len += ret; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED) 9262306a36Sopenharmony_ci len += nla_total_size(sizeof(u8)); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED) 9562306a36Sopenharmony_ci len += nla_total_size(sizeof(u8)); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return len; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int linkmodes_fill_reply(struct sk_buff *skb, 10162306a36Sopenharmony_ci const struct ethnl_req_info *req_base, 10262306a36Sopenharmony_ci const struct ethnl_reply_data *reply_base) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); 10562306a36Sopenharmony_ci const struct ethtool_link_ksettings *ksettings = &data->ksettings; 10662306a36Sopenharmony_ci const struct ethtool_link_settings *lsettings = &ksettings->base; 10762306a36Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg)) 11162306a36Sopenharmony_ci return -EMSGSIZE; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_OURS, 11462306a36Sopenharmony_ci ksettings->link_modes.advertising, 11562306a36Sopenharmony_ci ksettings->link_modes.supported, 11662306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, link_mode_names, 11762306a36Sopenharmony_ci compact); 11862306a36Sopenharmony_ci if (ret < 0) 11962306a36Sopenharmony_ci return -EMSGSIZE; 12062306a36Sopenharmony_ci if (!data->peer_empty) { 12162306a36Sopenharmony_ci ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_PEER, 12262306a36Sopenharmony_ci ksettings->link_modes.lp_advertising, 12362306a36Sopenharmony_ci NULL, __ETHTOOL_LINK_MODE_MASK_NBITS, 12462306a36Sopenharmony_ci link_mode_names, compact); 12562306a36Sopenharmony_ci if (ret < 0) 12662306a36Sopenharmony_ci return -EMSGSIZE; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) || 13062306a36Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex)) 13162306a36Sopenharmony_ci return -EMSGSIZE; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (ksettings->lanes && 13462306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_LINKMODES_LANES, ksettings->lanes)) 13562306a36Sopenharmony_ci return -EMSGSIZE; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED && 13862306a36Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, 13962306a36Sopenharmony_ci lsettings->master_slave_cfg)) 14062306a36Sopenharmony_ci return -EMSGSIZE; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED && 14362306a36Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, 14462306a36Sopenharmony_ci lsettings->master_slave_state)) 14562306a36Sopenharmony_ci return -EMSGSIZE; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_RATE_MATCHING, 14862306a36Sopenharmony_ci lsettings->rate_matching)) 14962306a36Sopenharmony_ci return -EMSGSIZE; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* LINKMODES_SET */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciconst struct nla_policy ethnl_linkmodes_set_policy[] = { 15762306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_HEADER] = 15862306a36Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 15962306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_AUTONEG] = { .type = NLA_U8 }, 16062306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_OURS] = { .type = NLA_NESTED }, 16162306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_U32 }, 16262306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_U8 }, 16362306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG] = { .type = NLA_U8 }, 16462306a36Sopenharmony_ci [ETHTOOL_A_LINKMODES_LANES] = NLA_POLICY_RANGE(NLA_U32, 1, 8), 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* Set advertised link modes to all supported modes matching requested speed, 16862306a36Sopenharmony_ci * lanes and duplex values. Called when autonegotiation is on, speed, lanes or 16962306a36Sopenharmony_ci * duplex is requested but no link mode change. This is done in userspace with 17062306a36Sopenharmony_ci * ioctl() interface, move it into kernel for netlink. 17162306a36Sopenharmony_ci * Returns true if advertised modes bitmap was modified. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings, 17462306a36Sopenharmony_ci bool req_speed, bool req_lanes, bool req_duplex) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci unsigned long *advertising = ksettings->link_modes.advertising; 17762306a36Sopenharmony_ci unsigned long *supported = ksettings->link_modes.supported; 17862306a36Sopenharmony_ci DECLARE_BITMAP(old_adv, __ETHTOOL_LINK_MODE_MASK_NBITS); 17962306a36Sopenharmony_ci unsigned int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci bitmap_copy(old_adv, advertising, __ETHTOOL_LINK_MODE_MASK_NBITS); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { 18462306a36Sopenharmony_ci const struct link_mode_info *info = &link_mode_params[i]; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (info->speed == SPEED_UNKNOWN) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci if (test_bit(i, supported) && 18962306a36Sopenharmony_ci (!req_speed || info->speed == ksettings->base.speed) && 19062306a36Sopenharmony_ci (!req_lanes || info->lanes == ksettings->lanes) && 19162306a36Sopenharmony_ci (!req_duplex || info->duplex == ksettings->base.duplex)) 19262306a36Sopenharmony_ci set_bit(i, advertising); 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci clear_bit(i, advertising); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return !bitmap_equal(old_adv, advertising, 19862306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic bool ethnl_validate_master_slave_cfg(u8 cfg) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci switch (cfg) { 20462306a36Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_PREFERRED: 20562306a36Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_PREFERRED: 20662306a36Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_FORCE: 20762306a36Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_FORCE: 20862306a36Sopenharmony_ci return true; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return false; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int ethnl_check_linkmodes(struct genl_info *info, struct nlattr **tb) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci const struct nlattr *master_slave_cfg, *lanes_cfg; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]; 21962306a36Sopenharmony_ci if (master_slave_cfg && 22062306a36Sopenharmony_ci !ethnl_validate_master_slave_cfg(nla_get_u8(master_slave_cfg))) { 22162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg, 22262306a36Sopenharmony_ci "master/slave value is invalid"); 22362306a36Sopenharmony_ci return -EOPNOTSUPP; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci lanes_cfg = tb[ETHTOOL_A_LINKMODES_LANES]; 22762306a36Sopenharmony_ci if (lanes_cfg && !is_power_of_2(nla_get_u32(lanes_cfg))) { 22862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, lanes_cfg, 22962306a36Sopenharmony_ci "lanes value is invalid"); 23062306a36Sopenharmony_ci return -EINVAL; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb, 23762306a36Sopenharmony_ci struct ethtool_link_ksettings *ksettings, 23862306a36Sopenharmony_ci bool *mod, const struct net_device *dev) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct ethtool_link_settings *lsettings = &ksettings->base; 24162306a36Sopenharmony_ci bool req_speed, req_lanes, req_duplex; 24262306a36Sopenharmony_ci const struct nlattr *master_slave_cfg, *lanes_cfg; 24362306a36Sopenharmony_ci int ret; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]; 24662306a36Sopenharmony_ci if (master_slave_cfg) { 24762306a36Sopenharmony_ci if (lsettings->master_slave_cfg == MASTER_SLAVE_CFG_UNSUPPORTED) { 24862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg, 24962306a36Sopenharmony_ci "master/slave configuration not supported by device"); 25062306a36Sopenharmony_ci return -EOPNOTSUPP; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci *mod = false; 25562306a36Sopenharmony_ci req_speed = tb[ETHTOOL_A_LINKMODES_SPEED]; 25662306a36Sopenharmony_ci req_lanes = tb[ETHTOOL_A_LINKMODES_LANES]; 25762306a36Sopenharmony_ci req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX]; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ethnl_update_u8(&lsettings->autoneg, tb[ETHTOOL_A_LINKMODES_AUTONEG], 26062306a36Sopenharmony_ci mod); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci lanes_cfg = tb[ETHTOOL_A_LINKMODES_LANES]; 26362306a36Sopenharmony_ci if (lanes_cfg) { 26462306a36Sopenharmony_ci /* If autoneg is off and lanes parameter is not supported by the 26562306a36Sopenharmony_ci * driver, return an error. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci if (!lsettings->autoneg && 26862306a36Sopenharmony_ci !dev->ethtool_ops->cap_link_lanes_supported) { 26962306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, lanes_cfg, 27062306a36Sopenharmony_ci "lanes configuration not supported by device"); 27162306a36Sopenharmony_ci return -EOPNOTSUPP; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } else if (!lsettings->autoneg && ksettings->lanes) { 27462306a36Sopenharmony_ci /* If autoneg is off and lanes parameter is not passed from user but 27562306a36Sopenharmony_ci * it was defined previously then set the lanes parameter to 0. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci ksettings->lanes = 0; 27862306a36Sopenharmony_ci *mod = true; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ret = ethnl_update_bitset(ksettings->link_modes.advertising, 28262306a36Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 28362306a36Sopenharmony_ci tb[ETHTOOL_A_LINKMODES_OURS], link_mode_names, 28462306a36Sopenharmony_ci info->extack, mod); 28562306a36Sopenharmony_ci if (ret < 0) 28662306a36Sopenharmony_ci return ret; 28762306a36Sopenharmony_ci ethnl_update_u32(&lsettings->speed, tb[ETHTOOL_A_LINKMODES_SPEED], 28862306a36Sopenharmony_ci mod); 28962306a36Sopenharmony_ci ethnl_update_u32(&ksettings->lanes, lanes_cfg, mod); 29062306a36Sopenharmony_ci ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX], 29162306a36Sopenharmony_ci mod); 29262306a36Sopenharmony_ci ethnl_update_u8(&lsettings->master_slave_cfg, master_slave_cfg, mod); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg && 29562306a36Sopenharmony_ci (req_speed || req_lanes || req_duplex) && 29662306a36Sopenharmony_ci ethnl_auto_linkmodes(ksettings, req_speed, req_lanes, req_duplex)) 29762306a36Sopenharmony_ci *mod = true; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int 30362306a36Sopenharmony_ciethnl_set_linkmodes_validate(struct ethnl_req_info *req_info, 30462306a36Sopenharmony_ci struct genl_info *info) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 30762306a36Sopenharmony_ci int ret; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = ethnl_check_linkmodes(info, info->attrs); 31062306a36Sopenharmony_ci if (ret < 0) 31162306a36Sopenharmony_ci return ret; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!ops->get_link_ksettings || !ops->set_link_ksettings) 31462306a36Sopenharmony_ci return -EOPNOTSUPP; 31562306a36Sopenharmony_ci return 1; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int 31962306a36Sopenharmony_ciethnl_set_linkmodes(struct ethnl_req_info *req_info, struct genl_info *info) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct ethtool_link_ksettings ksettings = {}; 32262306a36Sopenharmony_ci struct net_device *dev = req_info->dev; 32362306a36Sopenharmony_ci struct nlattr **tb = info->attrs; 32462306a36Sopenharmony_ci bool mod = false; 32562306a36Sopenharmony_ci int ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = __ethtool_get_link_ksettings(dev, &ksettings); 32862306a36Sopenharmony_ci if (ret < 0) { 32962306a36Sopenharmony_ci GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = ethnl_update_linkmodes(info, tb, &ksettings, &mod, dev); 33462306a36Sopenharmony_ci if (ret < 0) 33562306a36Sopenharmony_ci return ret; 33662306a36Sopenharmony_ci if (!mod) 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); 34062306a36Sopenharmony_ci if (ret < 0) { 34162306a36Sopenharmony_ci GENL_SET_ERR_MSG(info, "link settings update failed"); 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 1; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_linkmodes_request_ops = { 34962306a36Sopenharmony_ci .request_cmd = ETHTOOL_MSG_LINKMODES_GET, 35062306a36Sopenharmony_ci .reply_cmd = ETHTOOL_MSG_LINKMODES_GET_REPLY, 35162306a36Sopenharmony_ci .hdr_attr = ETHTOOL_A_LINKMODES_HEADER, 35262306a36Sopenharmony_ci .req_info_size = sizeof(struct linkmodes_req_info), 35362306a36Sopenharmony_ci .reply_data_size = sizeof(struct linkmodes_reply_data), 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci .prepare_data = linkmodes_prepare_data, 35662306a36Sopenharmony_ci .reply_size = linkmodes_reply_size, 35762306a36Sopenharmony_ci .fill_reply = linkmodes_fill_reply, 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci .set_validate = ethnl_set_linkmodes_validate, 36062306a36Sopenharmony_ci .set = ethnl_set_linkmodes, 36162306a36Sopenharmony_ci .set_ntf_cmd = ETHTOOL_MSG_LINKMODES_NTF, 36262306a36Sopenharmony_ci}; 363