xref: /openbmc/linux/net/ethtool/cabletest.c (revision 1dd3f212)
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