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