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