xref: /aosp_15_r20/external/ethtool/netlink/cable_test.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * cable_test.c - netlink implementation of cable test command
3  *
4  * Implementation of ethtool --cable-test <dev>
5  */
6 
7 #include <errno.h>
8 #include <string.h>
9 #include <stdio.h>
10 
11 #include "../internal.h"
12 #include "../common.h"
13 #include "netlink.h"
14 #include "parser.h"
15 
16 struct cable_test_context {
17 	bool breakout;
18 };
19 
nl_get_cable_test_result(const struct nlattr * nest,uint8_t * pair,uint16_t * code)20 static int nl_get_cable_test_result(const struct nlattr *nest, uint8_t *pair,
21 				    uint16_t *code)
22 {
23 	const struct nlattr *tb[ETHTOOL_A_CABLE_RESULT_MAX+1] = {};
24 	DECLARE_ATTR_TB_INFO(tb);
25 	int ret;
26 
27 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
28 	if (ret < 0 ||
29 	    !tb[ETHTOOL_A_CABLE_RESULT_PAIR] ||
30 	    !tb[ETHTOOL_A_CABLE_RESULT_CODE])
31 		return -EFAULT;
32 
33 	*pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_PAIR]);
34 	*code = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_RESULT_CODE]);
35 
36 	return 0;
37 }
38 
nl_get_cable_test_fault_length(const struct nlattr * nest,uint8_t * pair,unsigned int * cm)39 static int nl_get_cable_test_fault_length(const struct nlattr *nest,
40 					  uint8_t *pair, unsigned int *cm)
41 {
42 	const struct nlattr *tb[ETHTOOL_A_CABLE_FAULT_LENGTH_MAX+1] = {};
43 	DECLARE_ATTR_TB_INFO(tb);
44 	int ret;
45 
46 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
47 	if (ret < 0 ||
48 	    !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR] ||
49 	    !tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM])
50 		return -EFAULT;
51 
52 	*pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR]);
53 	*cm = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_FAULT_LENGTH_CM]);
54 
55 	return 0;
56 }
57 
nl_code2txt(uint16_t code)58 static char *nl_code2txt(uint16_t code)
59 {
60 	switch (code) {
61 	case ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC:
62 	default:
63 		return "Unknown";
64 	case ETHTOOL_A_CABLE_RESULT_CODE_OK:
65 		return "OK";
66 	case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
67 		return "Open Circuit";
68 	case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
69 		return "Short within Pair";
70 	case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
71 		return "Short to another pair";
72 	}
73 }
74 
nl_pair2txt(uint8_t pair)75 static char *nl_pair2txt(uint8_t pair)
76 {
77 	switch (pair) {
78 	case ETHTOOL_A_CABLE_PAIR_A:
79 		return "Pair A";
80 	case ETHTOOL_A_CABLE_PAIR_B:
81 		return "Pair B";
82 	case ETHTOOL_A_CABLE_PAIR_C:
83 		return "Pair C";
84 	case ETHTOOL_A_CABLE_PAIR_D:
85 		return "Pair D";
86 	default:
87 		return "Unexpected pair";
88 	}
89 }
90 
nl_cable_test_ntf_attr(struct nlattr * evattr)91 static int nl_cable_test_ntf_attr(struct nlattr *evattr)
92 {
93 	unsigned int cm;
94 	uint16_t code;
95 	uint8_t pair;
96 	int ret;
97 
98 	switch (mnl_attr_get_type(evattr)) {
99 	case ETHTOOL_A_CABLE_NEST_RESULT:
100 		ret = nl_get_cable_test_result(evattr, &pair, &code);
101 		if (ret < 0)
102 			return ret;
103 
104 		open_json_object(NULL);
105 		print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
106 		print_string(PRINT_ANY, "code", "code %s\n", nl_code2txt(code));
107 		close_json_object();
108 		break;
109 
110 	case ETHTOOL_A_CABLE_NEST_FAULT_LENGTH:
111 		ret = nl_get_cable_test_fault_length(evattr, &pair, &cm);
112 		if (ret < 0)
113 			return ret;
114 		open_json_object(NULL);
115 		print_string(PRINT_ANY, "pair", "%s, ", nl_pair2txt(pair));
116 		print_float(PRINT_ANY, "length", "fault length: %0.2fm\n",
117 			    (float)cm / 100);
118 		close_json_object();
119 		break;
120 	}
121 	return 0;
122 }
123 
cable_test_ntf_nest(const struct nlattr * nest)124 static void cable_test_ntf_nest(const struct nlattr *nest)
125 {
126 	struct nlattr *pos;
127 	int ret;
128 
129 	mnl_attr_for_each_nested(pos, nest) {
130 		ret = nl_cable_test_ntf_attr(pos);
131 		if (ret < 0)
132 			return;
133 	}
134 }
135 
136 /* Returns MNL_CB_STOP when the test is complete. Used when executing
137  * a test, but not suitable for monitor.
138  */
cable_test_ntf_stop_cb(const struct nlmsghdr * nlhdr,void * data)139 static int cable_test_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
140 {
141 	const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_NTF_MAX + 1] = {};
142 	u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
143 	struct cable_test_context *ctctx;
144 	struct nl_context *nlctx = data;
145 	DECLARE_ATTR_TB_INFO(tb);
146 	bool silent;
147 	int err_ret;
148 	int ret;
149 
150 	ctctx = nlctx->cmd_private;
151 
152 	silent = nlctx->is_dump || nlctx->is_monitor;
153 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
154 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
155 	if (ret < 0)
156 		return err_ret;
157 
158 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_HEADER]);
159 	if (!dev_ok(nlctx))
160 		return err_ret;
161 
162 	if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
163 		status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
164 
165 	switch (status) {
166 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
167 		print_string(PRINT_FP, "status",
168 			     "Cable test started for device %s.\n",
169 			     nlctx->devname);
170 		break;
171 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
172 		print_string(PRINT_FP, "status",
173 			     "Cable test completed for device %s.\n",
174 			     nlctx->devname);
175 		break;
176 	default:
177 		break;
178 	}
179 
180 	if (tb[ETHTOOL_A_CABLE_TEST_NTF_NEST])
181 		cable_test_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_NTF_NEST]);
182 
183 	if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
184 		if (ctctx)
185 			ctctx->breakout = true;
186 		return MNL_CB_STOP;
187 	}
188 
189 	return MNL_CB_OK;
190 }
191 
192 /* Wrapper around cable_test_ntf_stop_cb() which does not return STOP,
193  * used for monitor
194  */
cable_test_ntf_cb(const struct nlmsghdr * nlhdr,void * data)195 int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
196 {
197 	int status = cable_test_ntf_stop_cb(nlhdr, data);
198 
199 	if (status == MNL_CB_STOP)
200 		status = MNL_CB_OK;
201 
202 	return status;
203 }
204 
nl_cable_test_results_cb(const struct nlmsghdr * nlhdr,void * data)205 static int nl_cable_test_results_cb(const struct nlmsghdr *nlhdr, void *data)
206 {
207 	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
208 
209 	if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_NTF)
210 		return MNL_CB_OK;
211 
212 	return cable_test_ntf_stop_cb(nlhdr, data);
213 }
214 
215 /* Receive the broadcasted messages until we get the cable test
216  * results
217  */
nl_cable_test_process_results(struct cmd_context * ctx)218 static int nl_cable_test_process_results(struct cmd_context *ctx)
219 {
220 	struct nl_context *nlctx = ctx->nlctx;
221 	struct nl_socket *nlsk = nlctx->ethnl_socket;
222 	struct cable_test_context ctctx;
223 	int err;
224 
225 	nlctx->is_monitor = true;
226 	nlsk->port = 0;
227 	nlsk->seq = 0;
228 	nlctx->filter_devname = ctx->devname;
229 
230 	ctctx.breakout = false;
231 	nlctx->cmd_private = &ctctx;
232 
233 	while (!ctctx.breakout) {
234 		err = nlsock_process_reply(nlsk, nl_cable_test_results_cb,
235 					   nlctx);
236 		if (err)
237 			return err;
238 	}
239 
240 	return err;
241 }
242 
nl_cable_test(struct cmd_context * ctx)243 int nl_cable_test(struct cmd_context *ctx)
244 {
245 	struct nl_context *nlctx = ctx->nlctx;
246 	struct nl_socket *nlsk = nlctx->ethnl_socket;
247 	uint32_t grpid = nlctx->ethnl_mongrp;
248 	int ret;
249 
250 	/* Join the multicast group so we can receive the results in a
251 	 * race free way.
252 	 */
253 	if (!grpid) {
254 		fprintf(stderr, "multicast group 'monitor' not found\n");
255 		return -EOPNOTSUPP;
256 	}
257 
258 	ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
259 				    &grpid, sizeof(grpid));
260 	if (ret < 0)
261 		return ret;
262 
263 	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_CABLE_TEST_ACT,
264 				      ETHTOOL_A_CABLE_TEST_HEADER, 0);
265 	if (ret < 0)
266 		return ret;
267 
268 	ret = nlsock_sendmsg(nlsk, NULL);
269 	if (ret < 0)
270 		fprintf(stderr, "Cannot start cable test\n");
271 	else {
272 		new_json_obj(ctx->json);
273 
274 		ret = nl_cable_test_process_results(ctx);
275 
276 		delete_json_obj();
277 	}
278 
279 	return ret;
280 }
281 
nl_get_cable_test_tdr_amplitude(const struct nlattr * nest,uint8_t * pair,int16_t * mV)282 static int nl_get_cable_test_tdr_amplitude(const struct nlattr *nest,
283 					   uint8_t *pair, int16_t *mV)
284 {
285 	const struct nlattr *tb[ETHTOOL_A_CABLE_AMPLITUDE_MAX+1] = {};
286 	DECLARE_ATTR_TB_INFO(tb);
287 	uint16_t mV_unsigned;
288 	int ret;
289 
290 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
291 	if (ret < 0 ||
292 	    !tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR] ||
293 	    !tb[ETHTOOL_A_CABLE_AMPLITUDE_mV])
294 		return -EFAULT;
295 
296 	*pair = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_AMPLITUDE_PAIR]);
297 	mV_unsigned = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_AMPLITUDE_mV]);
298 	*mV = (int16_t)(mV_unsigned);
299 
300 	return 0;
301 }
302 
nl_get_cable_test_tdr_pulse(const struct nlattr * nest,uint16_t * mV)303 static int nl_get_cable_test_tdr_pulse(const struct nlattr *nest, uint16_t *mV)
304 {
305 	const struct nlattr *tb[ETHTOOL_A_CABLE_PULSE_MAX+1] = {};
306 	DECLARE_ATTR_TB_INFO(tb);
307 	int ret;
308 
309 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
310 	if (ret < 0 ||
311 	    !tb[ETHTOOL_A_CABLE_PULSE_mV])
312 		return -EFAULT;
313 
314 	*mV = mnl_attr_get_u16(tb[ETHTOOL_A_CABLE_PULSE_mV]);
315 
316 	return 0;
317 }
318 
nl_get_cable_test_tdr_step(const struct nlattr * nest,uint32_t * first,uint32_t * last,uint32_t * step)319 static int nl_get_cable_test_tdr_step(const struct nlattr *nest,
320 				      uint32_t *first, uint32_t *last,
321 				      uint32_t *step)
322 {
323 	const struct nlattr *tb[ETHTOOL_A_CABLE_STEP_MAX+1] = {};
324 	DECLARE_ATTR_TB_INFO(tb);
325 	int ret;
326 
327 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
328 	if (ret < 0 ||
329 	    !tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE] ||
330 	    !tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE] ||
331 	    !tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE])
332 		return -EFAULT;
333 
334 	*first = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE]);
335 	*last = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_LAST_DISTANCE]);
336 	*step = mnl_attr_get_u32(tb[ETHTOOL_A_CABLE_STEP_STEP_DISTANCE]);
337 
338 	return 0;
339 }
340 
nl_cable_test_tdr_ntf_attr(struct nlattr * evattr)341 static int nl_cable_test_tdr_ntf_attr(struct nlattr *evattr)
342 {
343 	uint32_t first, last, step;
344 	uint8_t pair;
345 	int ret;
346 
347 	switch (mnl_attr_get_type(evattr)) {
348 	case ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE: {
349 		int16_t mV;
350 
351 		ret = nl_get_cable_test_tdr_amplitude(
352 			evattr, &pair, &mV);
353 		if (ret < 0)
354 			return ret;
355 
356 		open_json_object(NULL);
357 		print_string(PRINT_ANY, "pair", "%s ", nl_pair2txt(pair));
358 		print_int(PRINT_ANY, "amplitude", "Amplitude %4d\n", mV);
359 		close_json_object();
360 		break;
361 	}
362 	case ETHTOOL_A_CABLE_TDR_NEST_PULSE: {
363 		uint16_t mV;
364 
365 		ret = nl_get_cable_test_tdr_pulse(evattr, &mV);
366 		if (ret < 0)
367 			return ret;
368 
369 		open_json_object(NULL);
370 		print_uint(PRINT_ANY, "pulse", "TDR Pulse %dmV\n", mV);
371 		close_json_object();
372 		break;
373 	}
374 	case ETHTOOL_A_CABLE_TDR_NEST_STEP:
375 		ret = nl_get_cable_test_tdr_step(evattr, &first, &last, &step);
376 		if (ret < 0)
377 			return ret;
378 
379 		open_json_object(NULL);
380 		print_float(PRINT_ANY, "first", "Step configuration: %.2f-",
381 			    (float)first / 100);
382 		print_float(PRINT_ANY, "last", "%.2f meters ",
383 			    (float)last / 100);
384 		print_float(PRINT_ANY, "step", "in %.2fm steps\n",
385 			    (float)step / 100);
386 		close_json_object();
387 		break;
388 	}
389 	return 0;
390 }
391 
cable_test_tdr_ntf_nest(const struct nlattr * nest)392 static void cable_test_tdr_ntf_nest(const struct nlattr *nest)
393 {
394 	struct nlattr *pos;
395 	int ret;
396 
397 	mnl_attr_for_each_nested(pos, nest) {
398 		ret = nl_cable_test_tdr_ntf_attr(pos);
399 		if (ret < 0)
400 			return;
401 	}
402 }
403 
404 /* Returns MNL_CB_STOP when the test is complete. Used when executing
405  * a test, but not suitable for monitor.
406  */
cable_test_tdr_ntf_stop_cb(const struct nlmsghdr * nlhdr,void * data)407 int cable_test_tdr_ntf_stop_cb(const struct nlmsghdr *nlhdr, void *data)
408 {
409 	const struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_MAX + 1] = {};
410 	u8 status = ETHTOOL_A_CABLE_TEST_NTF_STATUS_UNSPEC;
411 	struct cable_test_context *ctctx;
412 	struct nl_context *nlctx = data;
413 
414 	DECLARE_ATTR_TB_INFO(tb);
415 	bool silent;
416 	int err_ret;
417 	int ret;
418 
419 	ctctx = nlctx->cmd_private;
420 
421 	silent = nlctx->is_dump || nlctx->is_monitor;
422 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
423 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
424 	if (ret < 0)
425 		return err_ret;
426 
427 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER]);
428 	if (!dev_ok(nlctx))
429 		return err_ret;
430 
431 	if (tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS])
432 		status = mnl_attr_get_u8(tb[ETHTOOL_A_CABLE_TEST_NTF_STATUS]);
433 
434 	switch (status) {
435 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED:
436 		print_string(PRINT_FP, "status",
437 			     "Cable test TDR started for device %s.\n",
438 			     nlctx->devname);
439 		break;
440 	case ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED:
441 		print_string(PRINT_FP, "status",
442 			     "Cable test TDR completed for device %s.\n",
443 			     nlctx->devname);
444 		break;
445 	default:
446 		break;
447 	}
448 
449 	if (tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST])
450 		cable_test_tdr_ntf_nest(tb[ETHTOOL_A_CABLE_TEST_TDR_NTF_NEST]);
451 
452 	if (status == ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED) {
453 		if (ctctx)
454 			ctctx->breakout = true;
455 		return MNL_CB_STOP;
456 	}
457 
458 	return MNL_CB_OK;
459 }
460 
461 /* Wrapper around cable_test_tdr_ntf_stop_cb() which does not return
462  * STOP, used for monitor
463  */
cable_test_tdr_ntf_cb(const struct nlmsghdr * nlhdr,void * data)464 int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
465 {
466 	int status = cable_test_tdr_ntf_stop_cb(nlhdr, data);
467 
468 	if (status == MNL_CB_STOP)
469 		status = MNL_CB_OK;
470 
471 	return status;
472 }
473 
nl_cable_test_tdr_results_cb(const struct nlmsghdr * nlhdr,void * data)474 static int nl_cable_test_tdr_results_cb(const struct nlmsghdr *nlhdr,
475 					void *data)
476 {
477 	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
478 
479 	if (ghdr->cmd != ETHTOOL_MSG_CABLE_TEST_TDR_NTF)
480 		return MNL_CB_OK;
481 
482 	cable_test_tdr_ntf_cb(nlhdr, data);
483 
484 	return MNL_CB_STOP;
485 }
486 
487 /* Receive the broadcasted messages until we get the cable test
488  * results
489  */
nl_cable_test_tdr_process_results(struct cmd_context * ctx)490 static int nl_cable_test_tdr_process_results(struct cmd_context *ctx)
491 {
492 	struct nl_context *nlctx = ctx->nlctx;
493 	struct nl_socket *nlsk = nlctx->ethnl_socket;
494 	struct cable_test_context ctctx;
495 	int err;
496 
497 	nlctx->is_monitor = true;
498 	nlsk->port = 0;
499 	nlsk->seq = 0;
500 	nlctx->filter_devname = ctx->devname;
501 
502 	ctctx.breakout = false;
503 	nlctx->cmd_private = &ctctx;
504 
505 	while (!ctctx.breakout) {
506 		err = nlsock_process_reply(nlsk, nl_cable_test_tdr_results_cb,
507 					   nlctx);
508 		if (err)
509 			return err;
510 	}
511 
512 	return err;
513 }
514 
515 static const struct param_parser tdr_params[] = {
516 	{
517 		.arg		= "first",
518 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST,
519 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
520 		.handler	= nl_parse_direct_m2cm,
521 	},
522 	{
523 		.arg		= "last",
524 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST,
525 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
526 		.handler	= nl_parse_direct_m2cm,
527 	},
528 	{
529 		.arg		= "step",
530 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP,
531 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
532 		.handler	= nl_parse_direct_m2cm,
533 	},
534 	{
535 		.arg		= "pair",
536 		.type		= ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR,
537 		.group		= ETHTOOL_A_CABLE_TEST_TDR_CFG,
538 		.handler	= nl_parse_direct_u8,
539 	},
540 	{}
541 };
542 
nl_cable_test_tdr(struct cmd_context * ctx)543 int nl_cable_test_tdr(struct cmd_context *ctx)
544 {
545 	struct nl_context *nlctx = ctx->nlctx;
546 	struct nl_socket *nlsk = nlctx->ethnl_socket;
547 	uint32_t grpid = nlctx->ethnl_mongrp;
548 	struct nl_msg_buff *msgbuff;
549 	int ret;
550 
551 	nlctx->cmd = "--cable-test-tdr";
552 	nlctx->argp = ctx->argp;
553 	nlctx->argc = ctx->argc;
554 	nlctx->devname = ctx->devname;
555 	msgbuff = &nlsk->msgbuff;
556 
557 	/* Join the multicast group so we can receive the results in a
558 	 * race free way.
559 	 */
560 	if (!grpid) {
561 		fprintf(stderr, "multicast group 'monitor' not found\n");
562 		return -EOPNOTSUPP;
563 	}
564 
565 	ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
566 				    &grpid, sizeof(grpid));
567 	if (ret < 0)
568 		return ret;
569 
570 	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
571 		       NLM_F_REQUEST | NLM_F_ACK);
572 	if (ret < 0)
573 		return 2;
574 
575 	if (ethnla_fill_header(msgbuff, ETHTOOL_A_CABLE_TEST_TDR_HEADER,
576 			       ctx->devname, 0))
577 		return -EMSGSIZE;
578 
579 	ret = nl_parser(nlctx, tdr_params, NULL, PARSER_GROUP_NEST, NULL);
580 	if (ret < 0)
581 		return ret;
582 
583 	ret = nlsock_sendmsg(nlsk, NULL);
584 	if (ret < 0)
585 		fprintf(stderr, "Cannot start cable test TDR\n");
586 	else {
587 		new_json_obj(ctx->json);
588 
589 		ret = nl_cable_test_tdr_process_results(ctx);
590 
591 		delete_json_obj();
592 	}
593 
594 	return ret;
595 }
596