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 if (!dev->ethtool_ops->get_link_ksettings || 130 !dev->ethtool_ops->set_link_ksettings) 131 return -EOPNOTSUPP; 132 133 rtnl_lock(); 134 ret = ethnl_ops_begin(dev); 135 if (ret < 0) 136 goto out_rtnl; 137 138 ret = __ethtool_get_link_ksettings(dev, &ksettings); 139 if (ret < 0) { 140 if (info) 141 GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 142 goto out_ops; 143 } 144 lsettings = &ksettings.base; 145 146 ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod); 147 ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR], 148 &mod); 149 ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl, 150 tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod); 151 ret = 0; 152 if (!mod) 153 goto out_ops; 154 155 ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); 156 if (ret < 0) 157 GENL_SET_ERR_MSG(info, "link settings update failed"); 158 else 159 ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); 160 161 out_ops: 162 ethnl_ops_complete(dev); 163 out_rtnl: 164 rtnl_unlock(); 165 dev_put(dev); 166 return ret; 167 } 168