1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 6 struct linkinfo_req_info { 7 struct ethnl_req_info base; 8 }; 9 10 struct linkinfo_reply_data { 11 struct ethnl_reply_data base; 12 struct ethtool_link_ksettings ksettings; 13 struct ethtool_link_settings *lsettings; 14 }; 15 16 #define LINKINFO_REPDATA(__reply_base) \ 17 container_of(__reply_base, struct linkinfo_reply_data, base) 18 19 static const struct nla_policy 20 linkinfo_get_policy[ETHTOOL_A_LINKINFO_MAX + 1] = { 21 [ETHTOOL_A_LINKINFO_UNSPEC] = { .type = NLA_REJECT }, 22 [ETHTOOL_A_LINKINFO_HEADER] = { .type = NLA_NESTED }, 23 [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_REJECT }, 24 [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_REJECT }, 25 [ETHTOOL_A_LINKINFO_TP_MDIX] = { .type = NLA_REJECT }, 26 [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_REJECT }, 27 [ETHTOOL_A_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT }, 28 }; 29 30 static int linkinfo_prepare_data(const struct ethnl_req_info *req_base, 31 struct ethnl_reply_data *reply_base, 32 struct genl_info *info) 33 { 34 struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base); 35 struct net_device *dev = reply_base->dev; 36 int ret; 37 38 data->lsettings = &data->ksettings.base; 39 40 ret = ethnl_ops_begin(dev); 41 if (ret < 0) 42 return ret; 43 ret = __ethtool_get_link_ksettings(dev, &data->ksettings); 44 if (ret < 0 && info) 45 GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 46 ethnl_ops_complete(dev); 47 48 return ret; 49 } 50 51 static int linkinfo_reply_size(const struct ethnl_req_info *req_base, 52 const struct ethnl_reply_data *reply_base) 53 { 54 return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */ 55 + nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */ 56 + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */ 57 + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */ 58 + nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */ 59 + 0; 60 } 61 62 static int linkinfo_fill_reply(struct sk_buff *skb, 63 const struct ethnl_req_info *req_base, 64 const struct ethnl_reply_data *reply_base) 65 { 66 const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base); 67 68 if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) || 69 nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR, 70 data->lsettings->phy_address) || 71 nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX, 72 data->lsettings->eth_tp_mdix) || 73 nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, 74 data->lsettings->eth_tp_mdix_ctrl) || 75 nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER, 76 data->lsettings->transceiver)) 77 return -EMSGSIZE; 78 79 return 0; 80 } 81 82 const struct ethnl_request_ops ethnl_linkinfo_request_ops = { 83 .request_cmd = ETHTOOL_MSG_LINKINFO_GET, 84 .reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY, 85 .hdr_attr = ETHTOOL_A_LINKINFO_HEADER, 86 .max_attr = ETHTOOL_A_LINKINFO_MAX, 87 .req_info_size = sizeof(struct linkinfo_req_info), 88 .reply_data_size = sizeof(struct linkinfo_reply_data), 89 .request_policy = linkinfo_get_policy, 90 91 .prepare_data = linkinfo_prepare_data, 92 .reply_size = linkinfo_reply_size, 93 .fill_reply = linkinfo_fill_reply, 94 }; 95 96 /* LINKINFO_SET */ 97 98 static const struct nla_policy 99 linkinfo_set_policy[ETHTOOL_A_LINKINFO_MAX + 1] = { 100 [ETHTOOL_A_LINKINFO_UNSPEC] = { .type = NLA_REJECT }, 101 [ETHTOOL_A_LINKINFO_HEADER] = { .type = NLA_NESTED }, 102 [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 }, 103 [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 }, 104 [ETHTOOL_A_LINKINFO_TP_MDIX] = { .type = NLA_REJECT }, 105 [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 }, 106 [ETHTOOL_A_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT }, 107 }; 108 109 int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info) 110 { 111 struct nlattr *tb[ETHTOOL_A_LINKINFO_MAX + 1]; 112 struct ethtool_link_ksettings ksettings = {}; 113 struct ethtool_link_settings *lsettings; 114 struct ethnl_req_info req_info = {}; 115 struct net_device *dev; 116 bool mod = false; 117 int ret; 118 119 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 120 ETHTOOL_A_LINKINFO_MAX, linkinfo_set_policy, 121 info->extack); 122 if (ret < 0) 123 return ret; 124 ret = ethnl_parse_header(&req_info, tb[ETHTOOL_A_LINKINFO_HEADER], 125 genl_info_net(info), info->extack, true); 126 if (ret < 0) 127 return ret; 128 dev = req_info.dev; 129 ret = -EOPNOTSUPP; 130 if (!dev->ethtool_ops->get_link_ksettings || 131 !dev->ethtool_ops->set_link_ksettings) 132 goto out_dev; 133 134 rtnl_lock(); 135 ret = ethnl_ops_begin(dev); 136 if (ret < 0) 137 goto out_rtnl; 138 139 ret = __ethtool_get_link_ksettings(dev, &ksettings); 140 if (ret < 0) { 141 if (info) 142 GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 143 goto out_ops; 144 } 145 lsettings = &ksettings.base; 146 147 ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod); 148 ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR], 149 &mod); 150 ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl, 151 tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod); 152 ret = 0; 153 if (!mod) 154 goto out_ops; 155 156 ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); 157 if (ret < 0) 158 GENL_SET_ERR_MSG(info, "link settings update failed"); 159 else 160 ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); 161 162 out_ops: 163 ethnl_ops_complete(dev); 164 out_rtnl: 165 rtnl_unlock(); 166 out_dev: 167 dev_put(dev); 168 return ret; 169 } 170