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