• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * pse.c - netlink implementation of pse commands
3  *
4  * Implementation of "ethtool --show-pse <dev>" and
5  * "ethtool --set-pse <dev> ..."
6  */
7 
8 #include <errno.h>
9 #include <ctype.h>
10 #include <inttypes.h>
11 #include <string.h>
12 #include <stdio.h>
13 
14 #include "../internal.h"
15 #include "../common.h"
16 #include "netlink.h"
17 #include "parser.h"
18 
19 /* PSE_GET */
20 
podl_pse_admin_state_name(u32 val)21 static const char *podl_pse_admin_state_name(u32 val)
22 {
23 	switch (val) {
24 	case ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN:
25 		return "unknown";
26 	case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED:
27 		return "disabled";
28 	case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED:
29 		return "enabled";
30 	default:
31 		return "unsupported";
32 	}
33 }
34 
podl_pse_pw_d_status_name(u32 val)35 static const char *podl_pse_pw_d_status_name(u32 val)
36 {
37 	switch (val) {
38 	case ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN:
39 		return "unknown";
40 	case ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED:
41 		return "disabled";
42 	case ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING:
43 		return "searching";
44 	case ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING:
45 		return "delivering power";
46 	case ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP:
47 		return "sleep";
48 	case ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE:
49 		return "idle";
50 	case ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR:
51 		return "error";
52 	default:
53 		return "unsupported";
54 	}
55 }
56 
c33_pse_admin_state_name(u32 val)57 static const char *c33_pse_admin_state_name(u32 val)
58 {
59 	switch (val) {
60 	case ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN:
61 		return "unknown";
62 	case ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED:
63 		return "disabled";
64 	case ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED:
65 		return "enabled";
66 	default:
67 		return "unsupported";
68 	}
69 }
70 
c33_pse_pw_d_status_name(u32 val)71 static const char *c33_pse_pw_d_status_name(u32 val)
72 {
73 	switch (val) {
74 	case ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN:
75 		return "unknown";
76 	case ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED:
77 		return "disabled";
78 	case ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING:
79 		return "searching";
80 	case ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING:
81 		return "delivering power";
82 	case ETHTOOL_C33_PSE_PW_D_STATUS_TEST:
83 		return "test";
84 	case ETHTOOL_C33_PSE_PW_D_STATUS_FAULT:
85 		return "fault";
86 	case ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT:
87 		return "otherfault";
88 	default:
89 		return "unsupported";
90 	}
91 }
92 
c33_pse_ext_state_name(u32 val)93 static const char *c33_pse_ext_state_name(u32 val)
94 {
95 	switch (val) {
96 	case ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION:
97 		return "Group of error_condition states";
98 	case ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID:
99 		return "Group of mr_mps_valid states";
100 	case ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE:
101 		return "Group of mr_pse_enable states";
102 	case ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED:
103 		return "Group of option_detect_ted";
104 	case ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM:
105 		return "Group of option_vport_lim states";
106 	case ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED:
107 		return "Group of ovld_detected states";
108 	case ETHTOOL_C33_PSE_EXT_STATE_PD_DLL_POWER_TYPE:
109 		return "Group of pd_dll_power_type states";
110 	case ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE:
111 		return "Group of power_not_available states";
112 	case ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED:
113 		return "Group of short_detected states";
114 	default:
115 		return "unsupported";
116 	}
117 }
118 
c33_pse_ext_substate_mr_mps_valid_name(u32 val)119 static const char *c33_pse_ext_substate_mr_mps_valid_name(u32 val)
120 {
121 	switch (val) {
122 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_DETECTED_UNDERLOAD:
123 		return "Underload state";
124 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_CONNECTION_OPEN:
125 		return "Port is not connected";
126 	default:
127 		return "unsupported";
128 	}
129 }
130 
c33_pse_ext_substate_error_condition_name(u32 val)131 static const char *c33_pse_ext_substate_error_condition_name(u32 val)
132 {
133 	switch (val) {
134 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_NON_EXISTING_PORT:
135 		return "Non-existing port number";
136 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNDEFINED_PORT:
137 		return "Undefined port";
138 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT:
139 		return "Internal hardware fault";
140 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_COMM_ERROR_AFTER_FORCE_ON:
141 		return "Communication error after force on";
142 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS:
143 		return "Unknown port status";
144 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_TURN_OFF:
145 		return "Host crash turn off";
146 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_FORCE_SHUTDOWN:
147 		return "Host crash force shutdown";
148 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_CONFIG_CHANGE:
149 		return "Configuration change";
150 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP:
151 		return "Over temperature detected";
152 	default:
153 		return "unsupported";
154 	}
155 }
156 
c33_pse_ext_substate_mr_pse_enable_name(u32 val)157 static const char *c33_pse_ext_substate_mr_pse_enable_name(u32 val)
158 {
159 	switch (val) {
160 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_PSE_ENABLE_DISABLE_PIN_ACTIVE:
161 		return "Disable pin active";
162 	default:
163 		return "unsupported";
164 	}
165 }
166 
c33_pse_ext_substate_option_detect_ted_name(u32 val)167 static const char *c33_pse_ext_substate_option_detect_ted_name(u32 val)
168 {
169 	switch (val) {
170 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_DET_IN_PROCESS:
171 		return "Detection in process";
172 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_CONNECTION_CHECK_ERROR:
173 		return "Connection check error";
174 	default:
175 		return "unsupported";
176 	}
177 }
178 
c33_pse_ext_substate_option_vport_lim_name(u32 val)179 static const char *c33_pse_ext_substate_option_vport_lim_name(u32 val)
180 {
181 	switch (val) {
182 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_HIGH_VOLTAGE:
183 		return "Main supply voltage is high";
184 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_LOW_VOLTAGE:
185 		return "Main supply voltage is low";
186 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_VOLTAGE_INJECTION:
187 		return "Voltage injection into the port";
188 	default:
189 		return "unsupported";
190 	}
191 }
192 
c33_pse_ext_substate_ovld_detected_name(u32 val)193 static const char *c33_pse_ext_substate_ovld_detected_name(u32 val)
194 {
195 	switch (val) {
196 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_OVLD_DETECTED_OVERLOAD:
197 		return "Overload state";
198 	default:
199 		return "unsupported";
200 	}
201 }
202 
c33_pse_ext_substate_power_not_available_name(u32 val)203 static const char *c33_pse_ext_substate_power_not_available_name(u32 val)
204 {
205 	switch (val) {
206 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_BUDGET_EXCEEDED:
207 		return "Power budget exceeded for the controller";
208 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PORT_PW_LIMIT_EXCEEDS_CONTROLLER_BUDGET:
209 		return "Configured port power limit exceeded controller power budget";
210 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PD_REQUEST_EXCEEDS_PORT_LIMIT:
211 		return "Power request from PD exceeds port limit";
212 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_HW_PW_LIMIT:
213 		return "Power denied due to Hardware power limit";
214 	default:
215 		return "unsupported";
216 	}
217 }
218 
c33_pse_ext_substate_short_detected_name(u32 val)219 static const char *c33_pse_ext_substate_short_detected_name(u32 val)
220 {
221 	switch (val) {
222 	case ETHTOOL_C33_PSE_EXT_SUBSTATE_SHORT_DETECTED_SHORT_CONDITION:
223 		return "Short condition was detected";
224 	default:
225 		return "unsupported";
226 	}
227 }
228 
229 struct c33_pse_ext_substate_desc {
230 	u32 state;
231 	const char *(*substate_name)(u32 val);
232 };
233 
234 static const struct c33_pse_ext_substate_desc c33_pse_ext_substate_map[] = {
235 	{ ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
236 	  c33_pse_ext_substate_error_condition_name },
237 	{ ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID,
238 	  c33_pse_ext_substate_mr_mps_valid_name },
239 	{ ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE,
240 	  c33_pse_ext_substate_mr_pse_enable_name },
241 	{ ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED,
242 	  c33_pse_ext_substate_option_detect_ted_name },
243 	{ ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
244 	  c33_pse_ext_substate_option_vport_lim_name },
245 	{ ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED,
246 	  c33_pse_ext_substate_ovld_detected_name },
247 	{ ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
248 	  c33_pse_ext_substate_power_not_available_name },
249 	{ ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED,
250 	  c33_pse_ext_substate_short_detected_name },
251 	{ /* sentinel */ }
252 };
253 
c33_pse_print_ext_substate(u32 state,u32 substate)254 static void c33_pse_print_ext_substate(u32 state, u32 substate)
255 {
256 	const struct c33_pse_ext_substate_desc *substate_map;
257 
258 	substate_map = c33_pse_ext_substate_map;
259 	while (substate_map->state) {
260 		if (substate_map->state == state) {
261 			print_string(PRINT_ANY, "c33-pse-extended-substate",
262 				     "Clause 33 PSE Extended Substate: %s\n",
263 				     substate_map->substate_name(substate));
264 			return;
265 		}
266 		substate_map++;
267 	}
268 }
269 
c33_pse_dump_pw_limit_range(const struct nlattr * range)270 static int c33_pse_dump_pw_limit_range(const struct nlattr *range)
271 {
272 	const struct nlattr *range_tb[ETHTOOL_A_C33_PSE_PW_LIMIT_MAX + 1] = {};
273 	DECLARE_ATTR_TB_INFO(range_tb);
274 	const struct nlattr *attr;
275 	u32 min, max;
276 	int ret;
277 
278 	ret = mnl_attr_parse_nested(range, attr_cb, &range_tb_info);
279 	if (ret < 0) {
280 		fprintf(stderr,
281 			"malformed netlink message (power limit range)\n");
282 		return 1;
283 	}
284 
285 	attr = range_tb[ETHTOOL_A_C33_PSE_PW_LIMIT_MIN];
286 	if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
287 		fprintf(stderr,
288 			"malformed netlink message (power limit min)\n");
289 		return 1;
290 	}
291 	min = mnl_attr_get_u32(attr);
292 
293 	attr = range_tb[ETHTOOL_A_C33_PSE_PW_LIMIT_MAX];
294 	if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
295 		fprintf(stderr,
296 			"malformed netlink message (power limit max)\n");
297 		return 1;
298 	}
299 	max = mnl_attr_get_u32(attr);
300 
301 	print_string(PRINT_ANY, "range", "\trange:\n", NULL);
302 	print_uint(PRINT_ANY, "min", "\t\tmin %u\n", min);
303 	print_uint(PRINT_ANY, "max", "\t\tmax %u\n", max);
304 	return 0;
305 }
306 
pse_reply_cb(const struct nlmsghdr * nlhdr,void * data)307 int pse_reply_cb(const struct nlmsghdr *nlhdr, void *data)
308 {
309 	const struct nlattr *tb[ETHTOOL_A_PSE_MAX + 1] = {};
310 	struct nl_context *nlctx = data;
311 	const struct nlattr *attr;
312 	DECLARE_ATTR_TB_INFO(tb);
313 	bool silent;
314 	int err_ret;
315 	int ret;
316 
317 	silent = nlctx->is_dump || nlctx->is_monitor;
318 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
319 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
320 	if (ret < 0)
321 		return err_ret;
322 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_PSE_HEADER]);
323 	if (!dev_ok(nlctx))
324 		return err_ret;
325 
326 	if (silent)
327 		print_nl();
328 
329 	open_json_object(NULL);
330 
331 	print_string(PRINT_ANY, "ifname", "PSE attributes for %s:\n",
332 		     nlctx->devname);
333 
334 	if (tb[ETHTOOL_A_PODL_PSE_ADMIN_STATE]) {
335 		u32 val;
336 
337 		val = mnl_attr_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_STATE]);
338 		print_string(PRINT_ANY, "podl-pse-admin-state",
339 			     "PoDL PSE Admin State: %s\n",
340 			     podl_pse_admin_state_name(val));
341 	}
342 
343 	if (tb[ETHTOOL_A_PODL_PSE_PW_D_STATUS]) {
344 		u32 val;
345 
346 		val = mnl_attr_get_u32(tb[ETHTOOL_A_PODL_PSE_PW_D_STATUS]);
347 		print_string(PRINT_ANY, "podl-pse-power-detection-status",
348 			     "PoDL PSE Power Detection Status: %s\n",
349 			     podl_pse_pw_d_status_name(val));
350 	}
351 
352 	if (tb[ETHTOOL_A_C33_PSE_ADMIN_STATE]) {
353 		u32 val;
354 
355 		val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_STATE]);
356 		print_string(PRINT_ANY, "c33-pse-admin-state",
357 			     "Clause 33 PSE Admin State: %s\n",
358 			     c33_pse_admin_state_name(val));
359 	}
360 
361 	if (tb[ETHTOOL_A_C33_PSE_PW_D_STATUS]) {
362 		u32 val;
363 
364 		val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_PW_D_STATUS]);
365 		print_string(PRINT_ANY, "c33-pse-power-detection-status",
366 			     "Clause 33 PSE Power Detection Status: %s\n",
367 			     c33_pse_pw_d_status_name(val));
368 	}
369 
370 	if (tb[ETHTOOL_A_C33_PSE_EXT_STATE]) {
371 		u32 val;
372 
373 		val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_EXT_STATE]);
374 		print_string(PRINT_ANY, "c33-pse-extended-state",
375 			     "Clause 33 PSE Extended State: %s\n",
376 			     c33_pse_ext_state_name(val));
377 
378 		if (tb[ETHTOOL_A_C33_PSE_EXT_SUBSTATE]) {
379 			u32 substate;
380 
381 			substate = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_EXT_SUBSTATE]);
382 			c33_pse_print_ext_substate(val, substate);
383 		}
384 	}
385 
386 	if (tb[ETHTOOL_A_C33_PSE_PW_CLASS]) {
387 		u32 val;
388 
389 		val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_PW_CLASS]);
390 		print_uint(PRINT_ANY, "c33-pse-power-class",
391 			   "Clause 33 PSE Power Class: %u\n", val);
392 	}
393 
394 	if (tb[ETHTOOL_A_C33_PSE_ACTUAL_PW]) {
395 		u32 val;
396 
397 		val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_ACTUAL_PW]);
398 		print_uint(PRINT_ANY, "c33-pse-actual-power",
399 			   "Clause 33 PSE Actual Power: %u\n", val);
400 	}
401 
402 	if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) {
403 		u32 val;
404 
405 		val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]);
406 		print_uint(PRINT_ANY, "c33-pse-available-power-limit",
407 			   "Clause 33 PSE Available Power Limit: %u\n", val);
408 	}
409 
410 	if (tb[ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES]) {
411 		print_string(PRINT_ANY, "c33-pse-power-limit-ranges",
412 			     "Clause 33 PSE Power Limit Ranges:\n", NULL);
413 		mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
414 			if (mnl_attr_get_type(attr) == ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES) {
415 				if (c33_pse_dump_pw_limit_range(attr)) {
416 					close_json_object();
417 					return err_ret;
418 				}
419 			}
420 		}
421 	}
422 
423 	close_json_object();
424 
425 	return MNL_CB_OK;
426 }
427 
nl_gpse(struct cmd_context * ctx)428 int nl_gpse(struct cmd_context *ctx)
429 {
430 	struct nl_context *nlctx = ctx->nlctx;
431 	struct nl_socket *nlsk;
432 	int ret;
433 
434 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_PSE_GET, true))
435 		return -EOPNOTSUPP;
436 	if (ctx->argc > 0) {
437 		fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
438 			*ctx->argp);
439 		return 1;
440 	}
441 
442 	nlsk = nlctx->ethnl_socket;
443 	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PSE_GET,
444 				      ETHTOOL_A_PSE_HEADER, 0);
445 	if (ret < 0)
446 		return ret;
447 
448 	new_json_obj(ctx->json);
449 	ret = nlsock_send_get_request(nlsk, pse_reply_cb);
450 	delete_json_obj();
451 
452 	return ret;
453 }
454 
455 /* PSE_SET */
456 
457 static const struct lookup_entry_u32 podl_pse_admin_control_values[] = {
458 	{ .arg = "enable",	.val = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED },
459 	{ .arg = "disable",	.val = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED },
460 	{}
461 };
462 
463 static const struct lookup_entry_u32 c33_pse_admin_control_values[] = {
464 	{ .arg = "enable",	.val = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED },
465 	{ .arg = "disable",	.val = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED },
466 	{}
467 };
468 
469 static const struct param_parser spse_params[] = {
470 	{
471 		.arg		= "podl-pse-admin-control",
472 		.type		= ETHTOOL_A_PODL_PSE_ADMIN_CONTROL,
473 		.handler	= nl_parse_lookup_u32,
474 		.handler_data	= podl_pse_admin_control_values,
475 		.min_argc	= 1,
476 	},
477 	{
478 		.arg		= "c33-pse-admin-control",
479 		.type		= ETHTOOL_A_C33_PSE_ADMIN_CONTROL,
480 		.handler	= nl_parse_lookup_u32,
481 		.handler_data	= c33_pse_admin_control_values,
482 		.min_argc	= 1,
483 	},
484 	{
485 		.arg		= "c33-pse-avail-pw-limit",
486 		.type		= ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT,
487 		.handler	= nl_parse_direct_u32,
488 		.min_argc	= 1,
489 	},
490 	{}
491 };
492 
nl_spse(struct cmd_context * ctx)493 int nl_spse(struct cmd_context *ctx)
494 {
495 	struct nl_context *nlctx = ctx->nlctx;
496 	struct nl_msg_buff *msgbuff;
497 	struct nl_socket *nlsk;
498 	int ret;
499 
500 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_PSE_SET, false))
501 		return -EOPNOTSUPP;
502 	if (!ctx->argc) {
503 		fprintf(stderr, "ethtool (--set-pse): parameters missing\n");
504 		return 1;
505 	}
506 
507 	nlctx->cmd = "--set-pse";
508 	nlctx->argp = ctx->argp;
509 	nlctx->argc = ctx->argc;
510 	nlctx->devname = ctx->devname;
511 	nlsk = nlctx->ethnl_socket;
512 	msgbuff = &nlsk->msgbuff;
513 
514 	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PSE_SET,
515 		       NLM_F_REQUEST | NLM_F_ACK);
516 	if (ret < 0)
517 		return 2;
518 	if (ethnla_fill_header_phy(msgbuff, ETHTOOL_A_PSE_HEADER,
519 				   ctx->devname, ctx->phy_index, 0))
520 		return -EMSGSIZE;
521 
522 	ret = nl_parser(nlctx, spse_params, NULL, PARSER_GROUP_NONE, NULL);
523 	if (ret < 0)
524 		return 1;
525 
526 	ret = nlsock_sendmsg(nlsk, NULL);
527 	if (ret < 0)
528 		return 83;
529 	ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
530 	if (ret == 0)
531 		return 0;
532 	else
533 		return nlctx->exit_code ?: 83;
534 }
535