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 goto out; 86 87 return 0; 88 89 out: 90 nlmsg_free(phydev->skb); 91 return err; 92 } 93 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 94 95 void ethnl_cable_test_free(struct phy_device *phydev) 96 { 97 nlmsg_free(phydev->skb); 98 } 99 EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 100 101 void ethnl_cable_test_finished(struct phy_device *phydev) 102 { 103 nla_nest_end(phydev->skb, phydev->nest); 104 105 genlmsg_end(phydev->skb, phydev->ehdr); 106 107 ethnl_multicast(phydev->skb, phydev->attached_dev); 108 } 109 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 110