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