Linux Audio

Check our new training course

Loading...
v5.14.15
  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/* 802.3 standard allows 100 meters for BaseT cables. However longer
  9 * cables might work, depending on the quality of the cables and the
 10 * PHY. So allow testing for up to 150 meters.
 11 */
 12#define MAX_CABLE_LENGTH_CM (150 * 100)
 13
 14const struct nla_policy ethnl_cable_test_act_policy[] = {
 15	[ETHTOOL_A_CABLE_TEST_HEADER]		=
 16		NLA_POLICY_NESTED(ethnl_header_policy),
 
 17};
 18
 19static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
 20{
 21	struct sk_buff *skb;
 22	int err = -ENOMEM;
 23	void *ehdr;
 24
 25	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 26	if (!skb)
 27		goto out;
 28
 29	ehdr = ethnl_bcastmsg_put(skb, cmd);
 30	if (!ehdr) {
 31		err = -EMSGSIZE;
 32		goto out;
 33	}
 34
 35	err = ethnl_fill_reply_header(skb, phydev->attached_dev,
 36				      ETHTOOL_A_CABLE_TEST_NTF_HEADER);
 37	if (err)
 38		goto out;
 39
 40	err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
 41			 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED);
 42	if (err)
 43		goto out;
 44
 45	genlmsg_end(skb, ehdr);
 46
 47	return ethnl_multicast(skb, phydev->attached_dev);
 48
 49out:
 50	nlmsg_free(skb);
 51	phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err));
 52
 53	return err;
 54}
 55
 56int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
 57{
 
 58	struct ethnl_req_info req_info = {};
 59	const struct ethtool_phy_ops *ops;
 60	struct nlattr **tb = info->attrs;
 61	struct net_device *dev;
 62	int ret;
 63
 
 
 
 
 
 
 64	ret = ethnl_parse_header_dev_get(&req_info,
 65					 tb[ETHTOOL_A_CABLE_TEST_HEADER],
 66					 genl_info_net(info), info->extack,
 67					 true);
 68	if (ret < 0)
 69		return ret;
 70
 71	dev = req_info.dev;
 72	if (!dev->phydev) {
 73		ret = -EOPNOTSUPP;
 74		goto out_dev_put;
 75	}
 76
 77	rtnl_lock();
 78	ops = ethtool_phy_ops;
 79	if (!ops || !ops->start_cable_test) {
 80		ret = -EOPNOTSUPP;
 81		goto out_rtnl;
 82	}
 83
 84	ret = ethnl_ops_begin(dev);
 85	if (ret < 0)
 86		goto out_rtnl;
 87
 88	ret = ops->start_cable_test(dev->phydev, info->extack);
 89
 90	ethnl_ops_complete(dev);
 91
 92	if (!ret)
 93		ethnl_cable_test_started(dev->phydev,
 94					 ETHTOOL_MSG_CABLE_TEST_NTF);
 95
 96out_rtnl:
 97	rtnl_unlock();
 98out_dev_put:
 99	dev_put(dev);
100	return ret;
101}
102
103int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
104{
105	int err = -ENOMEM;
106
107	/* One TDR sample occupies 20 bytes. For a 150 meter cable,
108	 * with four pairs, around 12K is needed.
109	 */
110	phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL);
111	if (!phydev->skb)
112		goto out;
113
114	phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd);
115	if (!phydev->ehdr) {
116		err = -EMSGSIZE;
117		goto out;
118	}
119
120	err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev,
121				      ETHTOOL_A_CABLE_TEST_NTF_HEADER);
122	if (err)
123		goto out;
124
125	err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
126			 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED);
127	if (err)
128		goto out;
129
130	phydev->nest = nla_nest_start(phydev->skb,
131				      ETHTOOL_A_CABLE_TEST_NTF_NEST);
132	if (!phydev->nest) {
133		err = -EMSGSIZE;
134		goto out;
135	}
136
137	return 0;
138
139out:
140	nlmsg_free(phydev->skb);
141	phydev->skb = NULL;
142	return err;
143}
144EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc);
145
146void ethnl_cable_test_free(struct phy_device *phydev)
147{
148	nlmsg_free(phydev->skb);
149	phydev->skb = NULL;
150}
151EXPORT_SYMBOL_GPL(ethnl_cable_test_free);
152
153void ethnl_cable_test_finished(struct phy_device *phydev)
154{
155	nla_nest_end(phydev->skb, phydev->nest);
156
157	genlmsg_end(phydev->skb, phydev->ehdr);
158
159	ethnl_multicast(phydev->skb, phydev->attached_dev);
160}
161EXPORT_SYMBOL_GPL(ethnl_cable_test_finished);
162
163int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
164{
165	struct nlattr *nest;
166	int ret = -EMSGSIZE;
167
168	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT);
169	if (!nest)
170		return -EMSGSIZE;
171
172	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair))
173		goto err;
174	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result))
175		goto err;
176
177	nla_nest_end(phydev->skb, nest);
178	return 0;
179
180err:
181	nla_nest_cancel(phydev->skb, nest);
182	return ret;
183}
184EXPORT_SYMBOL_GPL(ethnl_cable_test_result);
185
186int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
187{
188	struct nlattr *nest;
189	int ret = -EMSGSIZE;
190
191	nest = nla_nest_start(phydev->skb,
192			      ETHTOOL_A_CABLE_NEST_FAULT_LENGTH);
193	if (!nest)
194		return -EMSGSIZE;
195
196	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair))
197		goto err;
198	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm))
199		goto err;
200
201	nla_nest_end(phydev->skb, nest);
202	return 0;
203
204err:
205	nla_nest_cancel(phydev->skb, nest);
206	return ret;
207}
208EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
209
210struct cable_test_tdr_req_info {
211	struct ethnl_req_info		base;
212};
213
214static const struct nla_policy cable_test_tdr_act_cfg_policy[] = {
 
215	[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]	= { .type = NLA_U32 },
216	[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]	= { .type = NLA_U32 },
217	[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]	= { .type = NLA_U32 },
218	[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]	= { .type = NLA_U8 },
219};
220
221const struct nla_policy ethnl_cable_test_tdr_act_policy[] = {
222	[ETHTOOL_A_CABLE_TEST_TDR_HEADER]	=
223		NLA_POLICY_NESTED(ethnl_header_policy),
 
224	[ETHTOOL_A_CABLE_TEST_TDR_CFG]		= { .type = NLA_NESTED },
225};
226
227/* CABLE_TEST_TDR_ACT */
228static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
229					struct genl_info *info,
230					struct phy_tdr_config *cfg)
231{
232	struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)];
233	int ret;
234
235	cfg->first = 100;
236	cfg->step = 100;
237	cfg->last = MAX_CABLE_LENGTH_CM;
238	cfg->pair = PHY_PAIR_ALL;
239
240	if (!nest)
241		return 0;
242
243	ret = nla_parse_nested(tb,
244			       ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1,
245			       nest, cable_test_tdr_act_cfg_policy,
246			       info->extack);
247	if (ret < 0)
248		return ret;
249
250	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST])
251		cfg->first = nla_get_u32(
252			tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]);
253
254	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST])
255		cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]);
256
257	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP])
258		cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]);
259
260	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) {
261		cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]);
262		if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) {
263			NL_SET_ERR_MSG_ATTR(
264				info->extack,
265				tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR],
266				"invalid pair parameter");
267			return -EINVAL;
268		}
269	}
270
271	if (cfg->first > MAX_CABLE_LENGTH_CM) {
272		NL_SET_ERR_MSG_ATTR(info->extack,
273				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST],
274				    "invalid first parameter");
275		return -EINVAL;
276	}
277
278	if (cfg->last > MAX_CABLE_LENGTH_CM) {
279		NL_SET_ERR_MSG_ATTR(info->extack,
280				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST],
281				    "invalid last parameter");
282		return -EINVAL;
283	}
284
285	if (cfg->first > cfg->last) {
286		NL_SET_ERR_MSG(info->extack, "invalid first/last parameter");
287		return -EINVAL;
288	}
289
290	if (!cfg->step) {
291		NL_SET_ERR_MSG_ATTR(info->extack,
292				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
293				    "invalid step parameter");
294		return -EINVAL;
295	}
296
297	if (cfg->step > (cfg->last - cfg->first)) {
298		NL_SET_ERR_MSG_ATTR(info->extack,
299				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
300				    "step parameter too big");
301		return -EINVAL;
302	}
303
304	return 0;
305}
306
307int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
308{
 
309	struct ethnl_req_info req_info = {};
310	const struct ethtool_phy_ops *ops;
311	struct nlattr **tb = info->attrs;
312	struct phy_tdr_config cfg;
313	struct net_device *dev;
314	int ret;
 
 
 
 
 
 
315
316	ret = ethnl_parse_header_dev_get(&req_info,
317					 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
318					 genl_info_net(info), info->extack,
319					 true);
320	if (ret < 0)
321		return ret;
322
323	dev = req_info.dev;
324	if (!dev->phydev) {
325		ret = -EOPNOTSUPP;
326		goto out_dev_put;
327	}
328
329	ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG],
330					   info, &cfg);
331	if (ret)
332		goto out_dev_put;
333
334	rtnl_lock();
335	ops = ethtool_phy_ops;
336	if (!ops || !ops->start_cable_test_tdr) {
337		ret = -EOPNOTSUPP;
338		goto out_rtnl;
339	}
340
341	ret = ethnl_ops_begin(dev);
342	if (ret < 0)
343		goto out_rtnl;
344
345	ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg);
346
347	ethnl_ops_complete(dev);
348
349	if (!ret)
350		ethnl_cable_test_started(dev->phydev,
351					 ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
352
353out_rtnl:
354	rtnl_unlock();
355out_dev_put:
356	dev_put(dev);
357	return ret;
358}
359 
360int ethnl_cable_test_amplitude(struct phy_device *phydev,
361			       u8 pair, s16 mV)
362{
363	struct nlattr *nest;
364	int ret = -EMSGSIZE;
365
366	nest = nla_nest_start(phydev->skb,
367			      ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE);
368	if (!nest)
369		return -EMSGSIZE;
370
371	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair))
372		goto err;
373	if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV))
374		goto err;
375
376	nla_nest_end(phydev->skb, nest);
377	return 0;
378
379err:
380	nla_nest_cancel(phydev->skb, nest);
381	return ret;
382}
383EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude);
384
385int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV)
386{
387	struct nlattr *nest;
388	int ret = -EMSGSIZE;
389
390	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE);
391	if (!nest)
392		return -EMSGSIZE;
393
394	if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV))
395		goto err;
396
397	nla_nest_end(phydev->skb, nest);
398	return 0;
399
400err:
401	nla_nest_cancel(phydev->skb, nest);
402	return ret;
403}
404EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse);
405
406int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last,
407			  u32 step)
408{
409	struct nlattr *nest;
410	int ret = -EMSGSIZE;
411
412	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP);
413	if (!nest)
414		return -EMSGSIZE;
415
416	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE,
417			first))
418		goto err;
419
420	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last))
421		goto err;
422
423	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step))
424		goto err;
425
426	nla_nest_end(phydev->skb, nest);
427	return 0;
428
429err:
430	nla_nest_cancel(phydev->skb, nest);
431	return ret;
432}
433EXPORT_SYMBOL_GPL(ethnl_cable_test_step);
v5.9
  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/* 802.3 standard allows 100 meters for BaseT cables. However longer
  9 * cables might work, depending on the quality of the cables and the
 10 * PHY. So allow testing for up to 150 meters.
 11 */
 12#define MAX_CABLE_LENGTH_CM (150 * 100)
 13
 14static const struct nla_policy
 15cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = {
 16	[ETHTOOL_A_CABLE_TEST_UNSPEC]		= { .type = NLA_REJECT },
 17	[ETHTOOL_A_CABLE_TEST_HEADER]		= { .type = NLA_NESTED },
 18};
 19
 20static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
 21{
 22	struct sk_buff *skb;
 23	int err = -ENOMEM;
 24	void *ehdr;
 25
 26	skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 27	if (!skb)
 28		goto out;
 29
 30	ehdr = ethnl_bcastmsg_put(skb, cmd);
 31	if (!ehdr) {
 32		err = -EMSGSIZE;
 33		goto out;
 34	}
 35
 36	err = ethnl_fill_reply_header(skb, phydev->attached_dev,
 37				      ETHTOOL_A_CABLE_TEST_NTF_HEADER);
 38	if (err)
 39		goto out;
 40
 41	err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
 42			 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED);
 43	if (err)
 44		goto out;
 45
 46	genlmsg_end(skb, ehdr);
 47
 48	return ethnl_multicast(skb, phydev->attached_dev);
 49
 50out:
 51	nlmsg_free(skb);
 52	phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err));
 53
 54	return err;
 55}
 56
 57int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
 58{
 59	struct nlattr *tb[ETHTOOL_A_CABLE_TEST_MAX + 1];
 60	struct ethnl_req_info req_info = {};
 61	const struct ethtool_phy_ops *ops;
 
 62	struct net_device *dev;
 63	int ret;
 64
 65	ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
 66			  ETHTOOL_A_CABLE_TEST_MAX,
 67			  cable_test_act_policy, info->extack);
 68	if (ret < 0)
 69		return ret;
 70
 71	ret = ethnl_parse_header_dev_get(&req_info,
 72					 tb[ETHTOOL_A_CABLE_TEST_HEADER],
 73					 genl_info_net(info), info->extack,
 74					 true);
 75	if (ret < 0)
 76		return ret;
 77
 78	dev = req_info.dev;
 79	if (!dev->phydev) {
 80		ret = -EOPNOTSUPP;
 81		goto out_dev_put;
 82	}
 83
 84	rtnl_lock();
 85	ops = ethtool_phy_ops;
 86	if (!ops || !ops->start_cable_test) {
 87		ret = -EOPNOTSUPP;
 88		goto out_rtnl;
 89	}
 90
 91	ret = ethnl_ops_begin(dev);
 92	if (ret < 0)
 93		goto out_rtnl;
 94
 95	ret = ops->start_cable_test(dev->phydev, info->extack);
 96
 97	ethnl_ops_complete(dev);
 98
 99	if (!ret)
100		ethnl_cable_test_started(dev->phydev,
101					 ETHTOOL_MSG_CABLE_TEST_NTF);
102
103out_rtnl:
104	rtnl_unlock();
105out_dev_put:
106	dev_put(dev);
107	return ret;
108}
109
110int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
111{
112	int err = -ENOMEM;
113
114	/* One TDR sample occupies 20 bytes. For a 150 meter cable,
115	 * with four pairs, around 12K is needed.
116	 */
117	phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL);
118	if (!phydev->skb)
119		goto out;
120
121	phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd);
122	if (!phydev->ehdr) {
123		err = -EMSGSIZE;
124		goto out;
125	}
126
127	err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev,
128				      ETHTOOL_A_CABLE_TEST_NTF_HEADER);
129	if (err)
130		goto out;
131
132	err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
133			 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED);
134	if (err)
135		goto out;
136
137	phydev->nest = nla_nest_start(phydev->skb,
138				      ETHTOOL_A_CABLE_TEST_NTF_NEST);
139	if (!phydev->nest) {
140		err = -EMSGSIZE;
141		goto out;
142	}
143
144	return 0;
145
146out:
147	nlmsg_free(phydev->skb);
148	phydev->skb = NULL;
149	return err;
150}
151EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc);
152
153void ethnl_cable_test_free(struct phy_device *phydev)
154{
155	nlmsg_free(phydev->skb);
156	phydev->skb = NULL;
157}
158EXPORT_SYMBOL_GPL(ethnl_cable_test_free);
159
160void ethnl_cable_test_finished(struct phy_device *phydev)
161{
162	nla_nest_end(phydev->skb, phydev->nest);
163
164	genlmsg_end(phydev->skb, phydev->ehdr);
165
166	ethnl_multicast(phydev->skb, phydev->attached_dev);
167}
168EXPORT_SYMBOL_GPL(ethnl_cable_test_finished);
169
170int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
171{
172	struct nlattr *nest;
173	int ret = -EMSGSIZE;
174
175	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT);
176	if (!nest)
177		return -EMSGSIZE;
178
179	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair))
180		goto err;
181	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result))
182		goto err;
183
184	nla_nest_end(phydev->skb, nest);
185	return 0;
186
187err:
188	nla_nest_cancel(phydev->skb, nest);
189	return ret;
190}
191EXPORT_SYMBOL_GPL(ethnl_cable_test_result);
192
193int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
194{
195	struct nlattr *nest;
196	int ret = -EMSGSIZE;
197
198	nest = nla_nest_start(phydev->skb,
199			      ETHTOOL_A_CABLE_NEST_FAULT_LENGTH);
200	if (!nest)
201		return -EMSGSIZE;
202
203	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair))
204		goto err;
205	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm))
206		goto err;
207
208	nla_nest_end(phydev->skb, nest);
209	return 0;
210
211err:
212	nla_nest_cancel(phydev->skb, nest);
213	return ret;
214}
215EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
216
217struct cable_test_tdr_req_info {
218	struct ethnl_req_info		base;
219};
220
221static const struct nla_policy
222cable_test_tdr_act_cfg_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1] = {
223	[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]	= { .type = NLA_U32 },
224	[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]	= { .type = NLA_U32 },
225	[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]	= { .type = NLA_U32 },
226	[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]	= { .type = NLA_U8 },
227};
228
229static const struct nla_policy
230cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1] = {
231	[ETHTOOL_A_CABLE_TEST_TDR_UNSPEC]	= { .type = NLA_REJECT },
232	[ETHTOOL_A_CABLE_TEST_TDR_HEADER]	= { .type = NLA_NESTED },
233	[ETHTOOL_A_CABLE_TEST_TDR_CFG]		= { .type = NLA_NESTED },
234};
235
236/* CABLE_TEST_TDR_ACT */
237static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
238					struct genl_info *info,
239					struct phy_tdr_config *cfg)
240{
241	struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1];
242	int ret;
243
244	cfg->first = 100;
245	cfg->step = 100;
246	cfg->last = MAX_CABLE_LENGTH_CM;
247	cfg->pair = PHY_PAIR_ALL;
248
249	if (!nest)
250		return 0;
251
252	ret = nla_parse_nested(tb, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX, nest,
253			       cable_test_tdr_act_cfg_policy, info->extack);
 
 
254	if (ret < 0)
255		return ret;
256
257	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST])
258		cfg->first = nla_get_u32(
259			tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]);
260
261	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST])
262		cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]);
263
264	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP])
265		cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]);
266
267	if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) {
268		cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]);
269		if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) {
270			NL_SET_ERR_MSG_ATTR(
271				info->extack,
272				tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR],
273				"invalid pair parameter");
274			return -EINVAL;
275		}
276	}
277
278	if (cfg->first > MAX_CABLE_LENGTH_CM) {
279		NL_SET_ERR_MSG_ATTR(info->extack,
280				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST],
281				    "invalid first parameter");
282		return -EINVAL;
283	}
284
285	if (cfg->last > MAX_CABLE_LENGTH_CM) {
286		NL_SET_ERR_MSG_ATTR(info->extack,
287				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST],
288				    "invalid last parameter");
289		return -EINVAL;
290	}
291
292	if (cfg->first > cfg->last) {
293		NL_SET_ERR_MSG(info->extack, "invalid first/last parameter");
294		return -EINVAL;
295	}
296
297	if (!cfg->step) {
298		NL_SET_ERR_MSG_ATTR(info->extack,
299				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
300				    "invalid step parameter");
301		return -EINVAL;
302	}
303
304	if (cfg->step > (cfg->last - cfg->first)) {
305		NL_SET_ERR_MSG_ATTR(info->extack,
306				    tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
307				    "step parameter too big");
308		return -EINVAL;
309	}
310
311	return 0;
312}
313
314int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
315{
316	struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1];
317	struct ethnl_req_info req_info = {};
318	const struct ethtool_phy_ops *ops;
 
319	struct phy_tdr_config cfg;
320	struct net_device *dev;
321	int ret;
322
323	ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
324			  ETHTOOL_A_CABLE_TEST_TDR_MAX,
325			  cable_test_tdr_act_policy, info->extack);
326	if (ret < 0)
327		return ret;
328
329	ret = ethnl_parse_header_dev_get(&req_info,
330					 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
331					 genl_info_net(info), info->extack,
332					 true);
333	if (ret < 0)
334		return ret;
335
336	dev = req_info.dev;
337	if (!dev->phydev) {
338		ret = -EOPNOTSUPP;
339		goto out_dev_put;
340	}
341
342	ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG],
343					   info, &cfg);
344	if (ret)
345		goto out_dev_put;
346
347	rtnl_lock();
348	ops = ethtool_phy_ops;
349	if (!ops || !ops->start_cable_test_tdr) {
350		ret = -EOPNOTSUPP;
351		goto out_rtnl;
352	}
353
354	ret = ethnl_ops_begin(dev);
355	if (ret < 0)
356		goto out_rtnl;
357
358	ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg);
359
360	ethnl_ops_complete(dev);
361
362	if (!ret)
363		ethnl_cable_test_started(dev->phydev,
364					 ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
365
366out_rtnl:
367	rtnl_unlock();
368out_dev_put:
369	dev_put(dev);
370	return ret;
371}
372 
373int ethnl_cable_test_amplitude(struct phy_device *phydev,
374			       u8 pair, s16 mV)
375{
376	struct nlattr *nest;
377	int ret = -EMSGSIZE;
378
379	nest = nla_nest_start(phydev->skb,
380			      ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE);
381	if (!nest)
382		return -EMSGSIZE;
383
384	if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair))
385		goto err;
386	if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV))
387		goto err;
388
389	nla_nest_end(phydev->skb, nest);
390	return 0;
391
392err:
393	nla_nest_cancel(phydev->skb, nest);
394	return ret;
395}
396EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude);
397
398int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV)
399{
400	struct nlattr *nest;
401	int ret = -EMSGSIZE;
402
403	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE);
404	if (!nest)
405		return -EMSGSIZE;
406
407	if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV))
408		goto err;
409
410	nla_nest_end(phydev->skb, nest);
411	return 0;
412
413err:
414	nla_nest_cancel(phydev->skb, nest);
415	return ret;
416}
417EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse);
418
419int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last,
420			  u32 step)
421{
422	struct nlattr *nest;
423	int ret = -EMSGSIZE;
424
425	nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP);
426	if (!nest)
427		return -EMSGSIZE;
428
429	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE,
430			first))
431		goto err;
432
433	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last))
434		goto err;
435
436	if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step))
437		goto err;
438
439	nla_nest_end(phydev->skb, nest);
440	return 0;
441
442err:
443	nla_nest_cancel(phydev->skb, nest);
444	return ret;
445}
446EXPORT_SYMBOL_GPL(ethnl_cable_test_step);