18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include "netlink.h" 48c2ecf20Sopenharmony_ci#include "common.h" 58c2ecf20Sopenharmony_ci#include "bitset.h" 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistruct linkmodes_req_info { 88c2ecf20Sopenharmony_ci struct ethnl_req_info base; 98c2ecf20Sopenharmony_ci}; 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistruct linkmodes_reply_data { 128c2ecf20Sopenharmony_ci struct ethnl_reply_data base; 138c2ecf20Sopenharmony_ci struct ethtool_link_ksettings ksettings; 148c2ecf20Sopenharmony_ci struct ethtool_link_settings *lsettings; 158c2ecf20Sopenharmony_ci bool peer_empty; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define LINKMODES_REPDATA(__reply_base) \ 198c2ecf20Sopenharmony_ci container_of(__reply_base, struct linkmodes_reply_data, base) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciconst struct nla_policy ethnl_linkmodes_get_policy[] = { 228c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_HEADER] = 238c2ecf20Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int linkmodes_prepare_data(const struct ethnl_req_info *req_base, 278c2ecf20Sopenharmony_ci struct ethnl_reply_data *reply_base, 288c2ecf20Sopenharmony_ci struct genl_info *info) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); 318c2ecf20Sopenharmony_ci struct net_device *dev = reply_base->dev; 328c2ecf20Sopenharmony_ci int ret; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci data->lsettings = &data->ksettings.base; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci ret = ethnl_ops_begin(dev); 378c2ecf20Sopenharmony_ci if (ret < 0) 388c2ecf20Sopenharmony_ci return ret; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ret = __ethtool_get_link_ksettings(dev, &data->ksettings); 418c2ecf20Sopenharmony_ci if (ret < 0 && info) { 428c2ecf20Sopenharmony_ci GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 438c2ecf20Sopenharmony_ci goto out; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci data->peer_empty = 478c2ecf20Sopenharmony_ci bitmap_empty(data->ksettings.link_modes.lp_advertising, 488c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciout: 518c2ecf20Sopenharmony_ci ethnl_ops_complete(dev); 528c2ecf20Sopenharmony_ci return ret; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int linkmodes_reply_size(const struct ethnl_req_info *req_base, 568c2ecf20Sopenharmony_ci const struct ethnl_reply_data *reply_base) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); 598c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *ksettings = &data->ksettings; 608c2ecf20Sopenharmony_ci const struct ethtool_link_settings *lsettings = &ksettings->base; 618c2ecf20Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 628c2ecf20Sopenharmony_ci int len, ret; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci len = nla_total_size(sizeof(u8)) /* LINKMODES_AUTONEG */ 658c2ecf20Sopenharmony_ci + nla_total_size(sizeof(u32)) /* LINKMODES_SPEED */ 668c2ecf20Sopenharmony_ci + nla_total_size(sizeof(u8)) /* LINKMODES_DUPLEX */ 678c2ecf20Sopenharmony_ci + 0; 688c2ecf20Sopenharmony_ci ret = ethnl_bitset_size(ksettings->link_modes.advertising, 698c2ecf20Sopenharmony_ci ksettings->link_modes.supported, 708c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 718c2ecf20Sopenharmony_ci link_mode_names, compact); 728c2ecf20Sopenharmony_ci if (ret < 0) 738c2ecf20Sopenharmony_ci return ret; 748c2ecf20Sopenharmony_ci len += ret; 758c2ecf20Sopenharmony_ci if (!data->peer_empty) { 768c2ecf20Sopenharmony_ci ret = ethnl_bitset_size(ksettings->link_modes.lp_advertising, 778c2ecf20Sopenharmony_ci NULL, __ETHTOOL_LINK_MODE_MASK_NBITS, 788c2ecf20Sopenharmony_ci link_mode_names, compact); 798c2ecf20Sopenharmony_ci if (ret < 0) 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci len += ret; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED) 858c2ecf20Sopenharmony_ci len += nla_total_size(sizeof(u8)); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED) 888c2ecf20Sopenharmony_ci len += nla_total_size(sizeof(u8)); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return len; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int linkmodes_fill_reply(struct sk_buff *skb, 948c2ecf20Sopenharmony_ci const struct ethnl_req_info *req_base, 958c2ecf20Sopenharmony_ci const struct ethnl_reply_data *reply_base) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); 988c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *ksettings = &data->ksettings; 998c2ecf20Sopenharmony_ci const struct ethtool_link_settings *lsettings = &ksettings->base; 1008c2ecf20Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg)) 1048c2ecf20Sopenharmony_ci return -EMSGSIZE; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_OURS, 1078c2ecf20Sopenharmony_ci ksettings->link_modes.advertising, 1088c2ecf20Sopenharmony_ci ksettings->link_modes.supported, 1098c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, link_mode_names, 1108c2ecf20Sopenharmony_ci compact); 1118c2ecf20Sopenharmony_ci if (ret < 0) 1128c2ecf20Sopenharmony_ci return -EMSGSIZE; 1138c2ecf20Sopenharmony_ci if (!data->peer_empty) { 1148c2ecf20Sopenharmony_ci ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_PEER, 1158c2ecf20Sopenharmony_ci ksettings->link_modes.lp_advertising, 1168c2ecf20Sopenharmony_ci NULL, __ETHTOOL_LINK_MODE_MASK_NBITS, 1178c2ecf20Sopenharmony_ci link_mode_names, compact); 1188c2ecf20Sopenharmony_ci if (ret < 0) 1198c2ecf20Sopenharmony_ci return -EMSGSIZE; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) || 1238c2ecf20Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex)) 1248c2ecf20Sopenharmony_ci return -EMSGSIZE; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED && 1278c2ecf20Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, 1288c2ecf20Sopenharmony_ci lsettings->master_slave_cfg)) 1298c2ecf20Sopenharmony_ci return -EMSGSIZE; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED && 1328c2ecf20Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, 1338c2ecf20Sopenharmony_ci lsettings->master_slave_state)) 1348c2ecf20Sopenharmony_ci return -EMSGSIZE; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciconst struct ethnl_request_ops ethnl_linkmodes_request_ops = { 1408c2ecf20Sopenharmony_ci .request_cmd = ETHTOOL_MSG_LINKMODES_GET, 1418c2ecf20Sopenharmony_ci .reply_cmd = ETHTOOL_MSG_LINKMODES_GET_REPLY, 1428c2ecf20Sopenharmony_ci .hdr_attr = ETHTOOL_A_LINKMODES_HEADER, 1438c2ecf20Sopenharmony_ci .req_info_size = sizeof(struct linkmodes_req_info), 1448c2ecf20Sopenharmony_ci .reply_data_size = sizeof(struct linkmodes_reply_data), 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci .prepare_data = linkmodes_prepare_data, 1478c2ecf20Sopenharmony_ci .reply_size = linkmodes_reply_size, 1488c2ecf20Sopenharmony_ci .fill_reply = linkmodes_fill_reply, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* LINKMODES_SET */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistruct link_mode_info { 1548c2ecf20Sopenharmony_ci int speed; 1558c2ecf20Sopenharmony_ci u8 duplex; 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#define __DEFINE_LINK_MODE_PARAMS(_speed, _type, _duplex) \ 1598c2ecf20Sopenharmony_ci [ETHTOOL_LINK_MODE(_speed, _type, _duplex)] = { \ 1608c2ecf20Sopenharmony_ci .speed = SPEED_ ## _speed, \ 1618c2ecf20Sopenharmony_ci .duplex = __DUPLEX_ ## _duplex \ 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci#define __DUPLEX_Half DUPLEX_HALF 1648c2ecf20Sopenharmony_ci#define __DUPLEX_Full DUPLEX_FULL 1658c2ecf20Sopenharmony_ci#define __DEFINE_SPECIAL_MODE_PARAMS(_mode) \ 1668c2ecf20Sopenharmony_ci [ETHTOOL_LINK_MODE_ ## _mode ## _BIT] = { \ 1678c2ecf20Sopenharmony_ci .speed = SPEED_UNKNOWN, \ 1688c2ecf20Sopenharmony_ci .duplex = DUPLEX_UNKNOWN, \ 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic const struct link_mode_info link_mode_params[] = { 1728c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10, T, Half), 1738c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10, T, Full), 1748c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100, T, Half), 1758c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100, T, Full), 1768c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(1000, T, Half), 1778c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(1000, T, Full), 1788c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(Autoneg), 1798c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(TP), 1808c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(AUI), 1818c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(MII), 1828c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(FIBRE), 1838c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(BNC), 1848c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, T, Full), 1858c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(Pause), 1868c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(Asym_Pause), 1878c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(2500, X, Full), 1888c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(Backplane), 1898c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(1000, KX, Full), 1908c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, KX4, Full), 1918c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, KR, Full), 1928c2ecf20Sopenharmony_ci [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = { 1938c2ecf20Sopenharmony_ci .speed = SPEED_10000, 1948c2ecf20Sopenharmony_ci .duplex = DUPLEX_FULL, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(20000, MLD2, Full), 1978c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(20000, KR2, Full), 1988c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(40000, KR4, Full), 1998c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(40000, CR4, Full), 2008c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(40000, SR4, Full), 2018c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(40000, LR4, Full), 2028c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(56000, KR4, Full), 2038c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(56000, CR4, Full), 2048c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(56000, SR4, Full), 2058c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(56000, LR4, Full), 2068c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(25000, CR, Full), 2078c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(25000, KR, Full), 2088c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(25000, SR, Full), 2098c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, CR2, Full), 2108c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, KR2, Full), 2118c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, KR4, Full), 2128c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, SR4, Full), 2138c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, CR4, Full), 2148c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, LR4_ER4, Full), 2158c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, SR2, Full), 2168c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(1000, X, Full), 2178c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, CR, Full), 2188c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, SR, Full), 2198c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, LR, Full), 2208c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, LRM, Full), 2218c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(10000, ER, Full), 2228c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(2500, T, Full), 2238c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(5000, T, Full), 2248c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(FEC_NONE), 2258c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(FEC_RS), 2268c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(FEC_BASER), 2278c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, KR, Full), 2288c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, SR, Full), 2298c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, CR, Full), 2308c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, LR_ER_FR, Full), 2318c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(50000, DR, Full), 2328c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, KR2, Full), 2338c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, SR2, Full), 2348c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, CR2, Full), 2358c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, LR2_ER2_FR2, Full), 2368c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, DR2, Full), 2378c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, KR4, Full), 2388c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, SR4, Full), 2398c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, LR4_ER4_FR4, Full), 2408c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, DR4, Full), 2418c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, CR4, Full), 2428c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100, T1, Full), 2438c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(1000, T1, Full), 2448c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, KR8, Full), 2458c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, SR8, Full), 2468c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, LR8_ER8_FR8, Full), 2478c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, DR8, Full), 2488c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, CR8, Full), 2498c2ecf20Sopenharmony_ci __DEFINE_SPECIAL_MODE_PARAMS(FEC_LLRS), 2508c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, KR, Full), 2518c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, SR, Full), 2528c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, LR_ER_FR, Full), 2538c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, DR, Full), 2548c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100000, CR, Full), 2558c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, KR2, Full), 2568c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, SR2, Full), 2578c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, LR2_ER2_FR2, Full), 2588c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, DR2, Full), 2598c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(200000, CR2, Full), 2608c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, KR4, Full), 2618c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, SR4, Full), 2628c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, LR4_ER4_FR4, Full), 2638c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, DR4, Full), 2648c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(400000, CR4, Full), 2658c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100, FX, Half), 2668c2ecf20Sopenharmony_ci __DEFINE_LINK_MODE_PARAMS(100, FX, Full), 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ciconst struct nla_policy ethnl_linkmodes_set_policy[] = { 2708c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_HEADER] = 2718c2ecf20Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 2728c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_AUTONEG] = { .type = NLA_U8 }, 2738c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_OURS] = { .type = NLA_NESTED }, 2748c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_U32 }, 2758c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_U8 }, 2768c2ecf20Sopenharmony_ci [ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG] = { .type = NLA_U8 }, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* Set advertised link modes to all supported modes matching requested speed 2808c2ecf20Sopenharmony_ci * and duplex values. Called when autonegotiation is on, speed or duplex is 2818c2ecf20Sopenharmony_ci * requested but no link mode change. This is done in userspace with ioctl() 2828c2ecf20Sopenharmony_ci * interface, move it into kernel for netlink. 2838c2ecf20Sopenharmony_ci * Returns true if advertised modes bitmap was modified. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_cistatic bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings, 2868c2ecf20Sopenharmony_ci bool req_speed, bool req_duplex) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci unsigned long *advertising = ksettings->link_modes.advertising; 2898c2ecf20Sopenharmony_ci unsigned long *supported = ksettings->link_modes.supported; 2908c2ecf20Sopenharmony_ci DECLARE_BITMAP(old_adv, __ETHTOOL_LINK_MODE_MASK_NBITS); 2918c2ecf20Sopenharmony_ci unsigned int i; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(link_mode_params) != 2948c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci bitmap_copy(old_adv, advertising, __ETHTOOL_LINK_MODE_MASK_NBITS); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci for (i = 0; i < __ETHTOOL_LINK_MODE_MASK_NBITS; i++) { 2998c2ecf20Sopenharmony_ci const struct link_mode_info *info = &link_mode_params[i]; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (info->speed == SPEED_UNKNOWN) 3028c2ecf20Sopenharmony_ci continue; 3038c2ecf20Sopenharmony_ci if (test_bit(i, supported) && 3048c2ecf20Sopenharmony_ci (!req_speed || info->speed == ksettings->base.speed) && 3058c2ecf20Sopenharmony_ci (!req_duplex || info->duplex == ksettings->base.duplex)) 3068c2ecf20Sopenharmony_ci set_bit(i, advertising); 3078c2ecf20Sopenharmony_ci else 3088c2ecf20Sopenharmony_ci clear_bit(i, advertising); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return !bitmap_equal(old_adv, advertising, 3128c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic bool ethnl_validate_master_slave_cfg(u8 cfg) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci switch (cfg) { 3188c2ecf20Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_PREFERRED: 3198c2ecf20Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_PREFERRED: 3208c2ecf20Sopenharmony_ci case MASTER_SLAVE_CFG_MASTER_FORCE: 3218c2ecf20Sopenharmony_ci case MASTER_SLAVE_CFG_SLAVE_FORCE: 3228c2ecf20Sopenharmony_ci return true; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return false; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb, 3298c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *ksettings, 3308c2ecf20Sopenharmony_ci bool *mod) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct ethtool_link_settings *lsettings = &ksettings->base; 3338c2ecf20Sopenharmony_ci bool req_speed, req_duplex; 3348c2ecf20Sopenharmony_ci const struct nlattr *master_slave_cfg; 3358c2ecf20Sopenharmony_ci int ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]; 3388c2ecf20Sopenharmony_ci if (master_slave_cfg) { 3398c2ecf20Sopenharmony_ci u8 cfg = nla_get_u8(master_slave_cfg); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (lsettings->master_slave_cfg == MASTER_SLAVE_CFG_UNSUPPORTED) { 3428c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg, 3438c2ecf20Sopenharmony_ci "master/slave configuration not supported by device"); 3448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (!ethnl_validate_master_slave_cfg(cfg)) { 3488c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg, 3498c2ecf20Sopenharmony_ci "master/slave value is invalid"); 3508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci *mod = false; 3558c2ecf20Sopenharmony_ci req_speed = tb[ETHTOOL_A_LINKMODES_SPEED]; 3568c2ecf20Sopenharmony_ci req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX]; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ethnl_update_u8(&lsettings->autoneg, tb[ETHTOOL_A_LINKMODES_AUTONEG], 3598c2ecf20Sopenharmony_ci mod); 3608c2ecf20Sopenharmony_ci ret = ethnl_update_bitset(ksettings->link_modes.advertising, 3618c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS, 3628c2ecf20Sopenharmony_ci tb[ETHTOOL_A_LINKMODES_OURS], link_mode_names, 3638c2ecf20Sopenharmony_ci info->extack, mod); 3648c2ecf20Sopenharmony_ci if (ret < 0) 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci ethnl_update_u32(&lsettings->speed, tb[ETHTOOL_A_LINKMODES_SPEED], 3678c2ecf20Sopenharmony_ci mod); 3688c2ecf20Sopenharmony_ci ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX], 3698c2ecf20Sopenharmony_ci mod); 3708c2ecf20Sopenharmony_ci ethnl_update_u8(&lsettings->master_slave_cfg, master_slave_cfg, mod); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg && 3738c2ecf20Sopenharmony_ci (req_speed || req_duplex) && 3748c2ecf20Sopenharmony_ci ethnl_auto_linkmodes(ksettings, req_speed, req_duplex)) 3758c2ecf20Sopenharmony_ci *mod = true; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciint ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct ethtool_link_ksettings ksettings = {}; 3838c2ecf20Sopenharmony_ci struct ethnl_req_info req_info = {}; 3848c2ecf20Sopenharmony_ci struct nlattr **tb = info->attrs; 3858c2ecf20Sopenharmony_ci struct net_device *dev; 3868c2ecf20Sopenharmony_ci bool mod = false; 3878c2ecf20Sopenharmony_ci int ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ret = ethnl_parse_header_dev_get(&req_info, 3908c2ecf20Sopenharmony_ci tb[ETHTOOL_A_LINKMODES_HEADER], 3918c2ecf20Sopenharmony_ci genl_info_net(info), info->extack, 3928c2ecf20Sopenharmony_ci true); 3938c2ecf20Sopenharmony_ci if (ret < 0) 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci dev = req_info.dev; 3968c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 3978c2ecf20Sopenharmony_ci if (!dev->ethtool_ops->get_link_ksettings || 3988c2ecf20Sopenharmony_ci !dev->ethtool_ops->set_link_ksettings) 3998c2ecf20Sopenharmony_ci goto out_dev; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci rtnl_lock(); 4028c2ecf20Sopenharmony_ci ret = ethnl_ops_begin(dev); 4038c2ecf20Sopenharmony_ci if (ret < 0) 4048c2ecf20Sopenharmony_ci goto out_rtnl; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = __ethtool_get_link_ksettings(dev, &ksettings); 4078c2ecf20Sopenharmony_ci if (ret < 0) { 4088c2ecf20Sopenharmony_ci GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 4098c2ecf20Sopenharmony_ci goto out_ops; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = ethnl_update_linkmodes(info, tb, &ksettings, &mod); 4138c2ecf20Sopenharmony_ci if (ret < 0) 4148c2ecf20Sopenharmony_ci goto out_ops; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (mod) { 4178c2ecf20Sopenharmony_ci ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); 4188c2ecf20Sopenharmony_ci if (ret < 0) 4198c2ecf20Sopenharmony_ci GENL_SET_ERR_MSG(info, "link settings update failed"); 4208c2ecf20Sopenharmony_ci else 4218c2ecf20Sopenharmony_ci ethtool_notify(dev, ETHTOOL_MSG_LINKMODES_NTF, NULL); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciout_ops: 4258c2ecf20Sopenharmony_ci ethnl_ops_complete(dev); 4268c2ecf20Sopenharmony_ciout_rtnl: 4278c2ecf20Sopenharmony_ci rtnl_unlock(); 4288c2ecf20Sopenharmony_ciout_dev: 4298c2ecf20Sopenharmony_ci dev_put(dev); 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci} 432