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_ci#define EEE_MODES_COUNT \ 88c2ecf20Sopenharmony_ci (sizeof_field(struct ethtool_eee, supported) * BITS_PER_BYTE) 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistruct eee_req_info { 118c2ecf20Sopenharmony_ci struct ethnl_req_info base; 128c2ecf20Sopenharmony_ci}; 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistruct eee_reply_data { 158c2ecf20Sopenharmony_ci struct ethnl_reply_data base; 168c2ecf20Sopenharmony_ci struct ethtool_eee eee; 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define EEE_REPDATA(__reply_base) \ 208c2ecf20Sopenharmony_ci container_of(__reply_base, struct eee_reply_data, base) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciconst struct nla_policy ethnl_eee_get_policy[] = { 238c2ecf20Sopenharmony_ci [ETHTOOL_A_EEE_HEADER] = 248c2ecf20Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int eee_prepare_data(const struct ethnl_req_info *req_base, 288c2ecf20Sopenharmony_ci struct ethnl_reply_data *reply_base, 298c2ecf20Sopenharmony_ci struct genl_info *info) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct eee_reply_data *data = EEE_REPDATA(reply_base); 328c2ecf20Sopenharmony_ci struct net_device *dev = reply_base->dev; 338c2ecf20Sopenharmony_ci int ret; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (!dev->ethtool_ops->get_eee) 368c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 378c2ecf20Sopenharmony_ci ret = ethnl_ops_begin(dev); 388c2ecf20Sopenharmony_ci if (ret < 0) 398c2ecf20Sopenharmony_ci return ret; 408c2ecf20Sopenharmony_ci ret = dev->ethtool_ops->get_eee(dev, &data->eee); 418c2ecf20Sopenharmony_ci ethnl_ops_complete(dev); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int eee_reply_size(const struct ethnl_req_info *req_base, 478c2ecf20Sopenharmony_ci const struct ethnl_reply_data *reply_base) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 508c2ecf20Sopenharmony_ci const struct eee_reply_data *data = EEE_REPDATA(reply_base); 518c2ecf20Sopenharmony_ci const struct ethtool_eee *eee = &data->eee; 528c2ecf20Sopenharmony_ci int len = 0; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE != 568c2ecf20Sopenharmony_ci EEE_MODES_COUNT); 578c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE != 588c2ecf20Sopenharmony_ci EEE_MODES_COUNT); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* MODES_OURS */ 618c2ecf20Sopenharmony_ci ret = ethnl_bitset32_size(&eee->advertised, &eee->supported, 628c2ecf20Sopenharmony_ci EEE_MODES_COUNT, link_mode_names, compact); 638c2ecf20Sopenharmony_ci if (ret < 0) 648c2ecf20Sopenharmony_ci return ret; 658c2ecf20Sopenharmony_ci len += ret; 668c2ecf20Sopenharmony_ci /* MODES_PEERS */ 678c2ecf20Sopenharmony_ci ret = ethnl_bitset32_size(&eee->lp_advertised, NULL, 688c2ecf20Sopenharmony_ci EEE_MODES_COUNT, link_mode_names, compact); 698c2ecf20Sopenharmony_ci if (ret < 0) 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci len += ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci len += nla_total_size(sizeof(u8)) + /* _EEE_ACTIVE */ 748c2ecf20Sopenharmony_ci nla_total_size(sizeof(u8)) + /* _EEE_ENABLED */ 758c2ecf20Sopenharmony_ci nla_total_size(sizeof(u8)) + /* _EEE_TX_LPI_ENABLED */ 768c2ecf20Sopenharmony_ci nla_total_size(sizeof(u32)); /* _EEE_TX_LPI_TIMER */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return len; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int eee_fill_reply(struct sk_buff *skb, 828c2ecf20Sopenharmony_ci const struct ethnl_req_info *req_base, 838c2ecf20Sopenharmony_ci const struct ethnl_reply_data *reply_base) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 868c2ecf20Sopenharmony_ci const struct eee_reply_data *data = EEE_REPDATA(reply_base); 878c2ecf20Sopenharmony_ci const struct ethtool_eee *eee = &data->eee; 888c2ecf20Sopenharmony_ci int ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS, 918c2ecf20Sopenharmony_ci &eee->advertised, &eee->supported, 928c2ecf20Sopenharmony_ci EEE_MODES_COUNT, link_mode_names, compact); 938c2ecf20Sopenharmony_ci if (ret < 0) 948c2ecf20Sopenharmony_ci return ret; 958c2ecf20Sopenharmony_ci ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER, 968c2ecf20Sopenharmony_ci &eee->lp_advertised, NULL, EEE_MODES_COUNT, 978c2ecf20Sopenharmony_ci link_mode_names, compact); 988c2ecf20Sopenharmony_ci if (ret < 0) 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) || 1028c2ecf20Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) || 1038c2ecf20Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED, 1048c2ecf20Sopenharmony_ci !!eee->tx_lpi_enabled) || 1058c2ecf20Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer)) 1068c2ecf20Sopenharmony_ci return -EMSGSIZE; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciconst struct ethnl_request_ops ethnl_eee_request_ops = { 1128c2ecf20Sopenharmony_ci .request_cmd = ETHTOOL_MSG_EEE_GET, 1138c2ecf20Sopenharmony_ci .reply_cmd = ETHTOOL_MSG_EEE_GET_REPLY, 1148c2ecf20Sopenharmony_ci .hdr_attr = ETHTOOL_A_EEE_HEADER, 1158c2ecf20Sopenharmony_ci .req_info_size = sizeof(struct eee_req_info), 1168c2ecf20Sopenharmony_ci .reply_data_size = sizeof(struct eee_reply_data), 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci .prepare_data = eee_prepare_data, 1198c2ecf20Sopenharmony_ci .reply_size = eee_reply_size, 1208c2ecf20Sopenharmony_ci .fill_reply = eee_fill_reply, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* EEE_SET */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ciconst struct nla_policy ethnl_eee_set_policy[] = { 1268c2ecf20Sopenharmony_ci [ETHTOOL_A_EEE_HEADER] = 1278c2ecf20Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 1288c2ecf20Sopenharmony_ci [ETHTOOL_A_EEE_MODES_OURS] = { .type = NLA_NESTED }, 1298c2ecf20Sopenharmony_ci [ETHTOOL_A_EEE_ENABLED] = { .type = NLA_U8 }, 1308c2ecf20Sopenharmony_ci [ETHTOOL_A_EEE_TX_LPI_ENABLED] = { .type = NLA_U8 }, 1318c2ecf20Sopenharmony_ci [ETHTOOL_A_EEE_TX_LPI_TIMER] = { .type = NLA_U32 }, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciint ethnl_set_eee(struct sk_buff *skb, struct genl_info *info) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct ethnl_req_info req_info = {}; 1378c2ecf20Sopenharmony_ci struct nlattr **tb = info->attrs; 1388c2ecf20Sopenharmony_ci const struct ethtool_ops *ops; 1398c2ecf20Sopenharmony_ci struct ethtool_eee eee = {}; 1408c2ecf20Sopenharmony_ci struct net_device *dev; 1418c2ecf20Sopenharmony_ci bool mod = false; 1428c2ecf20Sopenharmony_ci int ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = ethnl_parse_header_dev_get(&req_info, 1458c2ecf20Sopenharmony_ci tb[ETHTOOL_A_EEE_HEADER], 1468c2ecf20Sopenharmony_ci genl_info_net(info), info->extack, 1478c2ecf20Sopenharmony_ci true); 1488c2ecf20Sopenharmony_ci if (ret < 0) 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci dev = req_info.dev; 1518c2ecf20Sopenharmony_ci ops = dev->ethtool_ops; 1528c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 1538c2ecf20Sopenharmony_ci if (!ops->get_eee || !ops->set_eee) 1548c2ecf20Sopenharmony_ci goto out_dev; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci rtnl_lock(); 1578c2ecf20Sopenharmony_ci ret = ethnl_ops_begin(dev); 1588c2ecf20Sopenharmony_ci if (ret < 0) 1598c2ecf20Sopenharmony_ci goto out_rtnl; 1608c2ecf20Sopenharmony_ci ret = ops->get_eee(dev, &eee); 1618c2ecf20Sopenharmony_ci if (ret < 0) 1628c2ecf20Sopenharmony_ci goto out_ops; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT, 1658c2ecf20Sopenharmony_ci tb[ETHTOOL_A_EEE_MODES_OURS], 1668c2ecf20Sopenharmony_ci link_mode_names, info->extack, &mod); 1678c2ecf20Sopenharmony_ci if (ret < 0) 1688c2ecf20Sopenharmony_ci goto out_ops; 1698c2ecf20Sopenharmony_ci ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod); 1708c2ecf20Sopenharmony_ci ethnl_update_bool32(&eee.tx_lpi_enabled, 1718c2ecf20Sopenharmony_ci tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod); 1728c2ecf20Sopenharmony_ci ethnl_update_u32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER], 1738c2ecf20Sopenharmony_ci &mod); 1748c2ecf20Sopenharmony_ci ret = 0; 1758c2ecf20Sopenharmony_ci if (!mod) 1768c2ecf20Sopenharmony_ci goto out_ops; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = dev->ethtool_ops->set_eee(dev, &eee); 1798c2ecf20Sopenharmony_ci if (ret < 0) 1808c2ecf20Sopenharmony_ci goto out_ops; 1818c2ecf20Sopenharmony_ci ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciout_ops: 1848c2ecf20Sopenharmony_ci ethnl_ops_complete(dev); 1858c2ecf20Sopenharmony_ciout_rtnl: 1868c2ecf20Sopenharmony_ci rtnl_unlock(); 1878c2ecf20Sopenharmony_ciout_dev: 1888c2ecf20Sopenharmony_ci dev_put(dev); 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci} 191