1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/phy.h> 4 #include <linux/ethtool_netlink.h> 5 #include "netlink.h" 6 #include "common.h" 7 8 /* CABLE_TEST_ACT */ 9 10 static const struct nla_policy 11 cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = { 12 [ETHTOOL_A_CABLE_TEST_UNSPEC] = { .type = NLA_REJECT }, 13 [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, 14 }; 15 16 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 17 { 18 struct nlattr *tb[ETHTOOL_A_CABLE_TEST_MAX + 1]; 19 struct ethnl_req_info req_info = {}; 20 struct net_device *dev; 21 int ret; 22 23 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 24 ETHTOOL_A_CABLE_TEST_MAX, 25 cable_test_act_policy, info->extack); 26 if (ret < 0) 27 return ret; 28 29 ret = ethnl_parse_header_dev_get(&req_info, 30 tb[ETHTOOL_A_CABLE_TEST_HEADER], 31 genl_info_net(info), info->extack, 32 true); 33 if (ret < 0) 34 return ret; 35 36 dev = req_info.dev; 37 if (!dev->phydev) { 38 ret = -EOPNOTSUPP; 39 goto out_dev_put; 40 } 41 42 rtnl_lock(); 43 ret = ethnl_ops_begin(dev); 44 if (ret < 0) 45 goto out_rtnl; 46 47 ret = phy_start_cable_test(dev->phydev, info->extack); 48 49 ethnl_ops_complete(dev); 50 out_rtnl: 51 rtnl_unlock(); 52 out_dev_put: 53 dev_put(dev); 54 return ret; 55 } 56 57 int ethnl_cable_test_alloc(struct phy_device *phydev) 58 { 59 int err = -ENOMEM; 60 61 phydev->skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 62 if (!phydev->skb) 63 goto out; 64 65 phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, 66 ETHTOOL_MSG_CABLE_TEST_NTF); 67 if (!phydev->ehdr) { 68 err = -EMSGSIZE; 69 goto out; 70 } 71 72 err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev, 73 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 74 if (err) 75 goto out; 76 77 err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 78 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED); 79 if (err) 80 goto out; 81 82 phydev->nest = nla_nest_start(phydev->skb, 83 ETHTOOL_A_CABLE_TEST_NTF_NEST); 84 if (!phydev->nest) { 85 err = -EMSGSIZE; 86 goto out; 87 } 88 89 return 0; 90 91 out: 92 nlmsg_free(phydev->skb); 93 phydev->skb = NULL; 94 return err; 95 } 96 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 97 98 void ethnl_cable_test_free(struct phy_device *phydev) 99 { 100 nlmsg_free(phydev->skb); 101 phydev->skb = NULL; 102 } 103 EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 104 105 void ethnl_cable_test_finished(struct phy_device *phydev) 106 { 107 nla_nest_end(phydev->skb, phydev->nest); 108 109 genlmsg_end(phydev->skb, phydev->ehdr); 110 111 ethnl_multicast(phydev->skb, phydev->attached_dev); 112 } 113 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 114 115 int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result) 116 { 117 struct nlattr *nest; 118 int ret = -EMSGSIZE; 119 120 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT); 121 if (!nest) 122 return -EMSGSIZE; 123 124 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair)) 125 goto err; 126 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result)) 127 goto err; 128 129 nla_nest_end(phydev->skb, nest); 130 return 0; 131 132 err: 133 nla_nest_cancel(phydev->skb, nest); 134 return ret; 135 } 136 EXPORT_SYMBOL_GPL(ethnl_cable_test_result); 137 138 int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm) 139 { 140 struct nlattr *nest; 141 int ret = -EMSGSIZE; 142 143 nest = nla_nest_start(phydev->skb, 144 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 145 if (!nest) 146 return -EMSGSIZE; 147 148 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 149 goto err; 150 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 151 goto err; 152 153 nla_nest_end(phydev->skb, nest); 154 return 0; 155 156 err: 157 nla_nest_cancel(phydev->skb, nest); 158 return ret; 159 } 160 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); 161