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 const struct nla_policy ethnl_linkinfo_get_policy[] = { 20 [ETHTOOL_A_LINKINFO_HEADER] = 21 NLA_POLICY_NESTED(ethnl_header_policy), 22 }; 23 24 static int linkinfo_prepare_data(const struct ethnl_req_info *req_base, 25 struct ethnl_reply_data *reply_base, 26 const struct genl_info *info) 27 { 28 struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base); 29 struct net_device *dev = reply_base->dev; 30 int ret; 31 32 data->lsettings = &data->ksettings.base; 33 34 ret = ethnl_ops_begin(dev); 35 if (ret < 0) 36 return ret; 37 ret = __ethtool_get_link_ksettings(dev, &data->ksettings); 38 if (ret < 0) 39 GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 40 ethnl_ops_complete(dev); 41 42 return ret; 43 } 44 45 static int linkinfo_reply_size(const struct ethnl_req_info *req_base, 46 const struct ethnl_reply_data *reply_base) 47 { 48 return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */ 49 + nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */ 50 + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */ 51 + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */ 52 + nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */ 53 + 0; 54 } 55 56 static int linkinfo_fill_reply(struct sk_buff *skb, 57 const struct ethnl_req_info *req_base, 58 const struct ethnl_reply_data *reply_base) 59 { 60 const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base); 61 62 if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) || 63 nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR, 64 data->lsettings->phy_address) || 65 nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX, 66 data->lsettings->eth_tp_mdix) || 67 nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL, 68 data->lsettings->eth_tp_mdix_ctrl) || 69 nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER, 70 data->lsettings->transceiver)) 71 return -EMSGSIZE; 72 73 return 0; 74 } 75 76 /* LINKINFO_SET */ 77 78 const struct nla_policy ethnl_linkinfo_set_policy[] = { 79 [ETHTOOL_A_LINKINFO_HEADER] = 80 NLA_POLICY_NESTED(ethnl_header_policy), 81 [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 }, 82 [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 }, 83 [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 }, 84 }; 85 86 static int 87 ethnl_set_linkinfo_validate(struct ethnl_req_info *req_info, 88 struct genl_info *info) 89 { 90 const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 91 92 if (!ops->get_link_ksettings || !ops->set_link_ksettings) 93 return -EOPNOTSUPP; 94 return 1; 95 } 96 97 static int 98 ethnl_set_linkinfo(struct ethnl_req_info *req_info, struct genl_info *info) 99 { 100 struct ethtool_link_ksettings ksettings = {}; 101 struct ethtool_link_settings *lsettings; 102 struct net_device *dev = req_info->dev; 103 struct nlattr **tb = info->attrs; 104 bool mod = false; 105 int ret; 106 107 ret = __ethtool_get_link_ksettings(dev, &ksettings); 108 if (ret < 0) { 109 GENL_SET_ERR_MSG(info, "failed to retrieve link settings"); 110 return ret; 111 } 112 lsettings = &ksettings.base; 113 114 ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod); 115 ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR], 116 &mod); 117 ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl, 118 tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod); 119 if (!mod) 120 return 0; 121 122 ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings); 123 if (ret < 0) { 124 GENL_SET_ERR_MSG(info, "link settings update failed"); 125 return ret; 126 } 127 128 return 1; 129 } 130 131 const struct ethnl_request_ops ethnl_linkinfo_request_ops = { 132 .request_cmd = ETHTOOL_MSG_LINKINFO_GET, 133 .reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY, 134 .hdr_attr = ETHTOOL_A_LINKINFO_HEADER, 135 .req_info_size = sizeof(struct linkinfo_req_info), 136 .reply_data_size = sizeof(struct linkinfo_reply_data), 137 138 .prepare_data = linkinfo_prepare_data, 139 .reply_size = linkinfo_reply_size, 140 .fill_reply = linkinfo_fill_reply, 141 142 .set_validate = ethnl_set_linkinfo_validate, 143 .set = ethnl_set_linkinfo, 144 .set_ntf_cmd = ETHTOOL_MSG_LINKINFO_NTF, 145 }; 146