[PATCH ethtool-next 1/3] ethtool: pse-pd: Expand C33 PSE with several new features

From: Kory Maincent
Date: Thu Sep 12 2024 - 05:20:41 EST


From: Kory Maincent (Dent Project) <kory.maincent@xxxxxxxxxxx>

This patch adds support for several new features to the C33 PSE commands:
- Get the Class negotiated between the Powered Device and the PSE
- Get Extended state and substate
- Get the Actual power
- Configure the power limit
- Get the Power limit ranges available

Example:
$ ethtool --set-pse eth1 c33-pse-avail-pw-limit 18000
$ ethtool --show-pse eth1
PSE attributes for eth1:
Clause 33 PSE Admin State: enabled
Clause 33 PSE Power Detection Status: disabled
Clause 33 PSE Extended State: Group of mr_mps_valid states
Clause 33 PSE Extended Substate: Port is not connected
Clause 33 PSE Available Power Limit: 18000
Clause 33 PSE Power Limit Ranges:
range:
min 15000
max 18100
range:
min 30000
max 38000
range:
min 60000
max 65000
range:
min 90000
max 97500

Signed-off-by: Kory Maincent <kory.maincent@xxxxxxxxxxx>
---

If you think the Power Limit Ranges display does not fit your liking
please do not hesitate to ask for change.
---
netlink/pse-pd.c | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 275 insertions(+)

diff --git a/netlink/pse-pd.c b/netlink/pse-pd.c
index 3f6b6aa..fd1fc4d 100644
--- a/netlink/pse-pd.c
+++ b/netlink/pse-pd.c
@@ -89,10 +89,226 @@ static const char *c33_pse_pw_d_status_name(u32 val)
return "unsupported";
}
}
+
+static const char *c33_pse_ext_state_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION:
+ return "Group of error_condition states";
+ case ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID:
+ return "Group of mr_mps_valid states";
+ case ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE:
+ return "Group of mr_pse_enable states";
+ case ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED:
+ return "Group of option_detect_ted";
+ case ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM:
+ return "Group of option_vport_lim states";
+ case ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED:
+ return "Group of ovld_detected states";
+ case ETHTOOL_C33_PSE_EXT_STATE_PD_DLL_POWER_TYPE:
+ return "Group of pd_dll_power_type states";
+ case ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE:
+ return "Group of power_not_available states";
+ case ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED:
+ return "Group of short_detected states";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_mr_mps_valid_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_DETECTED_UNDERLOAD:
+ return "Underload state";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_CONNECTION_OPEN:
+ return "Port is not connected";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_error_condition_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_NON_EXISTING_PORT:
+ return "Non-existing port number";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNDEFINED_PORT:
+ return "Undefined port";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT:
+ return "Internal hardware fault";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_COMM_ERROR_AFTER_FORCE_ON:
+ return "Communication error after force on";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS:
+ return "Unknown port status";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_TURN_OFF:
+ return "Host crash turn off";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_FORCE_SHUTDOWN:
+ return "Host crash force shutdown";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_CONFIG_CHANGE:
+ return "Configuration change";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP:
+ return "Over temperature detected";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_mr_pse_enable_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_PSE_ENABLE_DISABLE_PIN_ACTIVE:
+ return "Disable pin active";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_option_detect_ted_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_DET_IN_PROCESS:
+ return "Detection in process";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_CONNECTION_CHECK_ERROR:
+ return "Connection check error";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_option_vport_lim_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_HIGH_VOLTAGE:
+ return "Main supply voltage is high";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_LOW_VOLTAGE:
+ return "Main supply voltage is low";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_VOLTAGE_INJECTION:
+ return "Voltage injection into the port";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_ovld_detected_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_OVLD_DETECTED_OVERLOAD:
+ return "Overload state";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_power_not_available_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_BUDGET_EXCEEDED:
+ return "Power budget exceeded for the controller";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PORT_PW_LIMIT_EXCEEDS_CONTROLLER_BUDGET:
+ return "Configured port power limit exceeded controller power budget";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PD_REQUEST_EXCEEDS_PORT_LIMIT:
+ return "Power request from PD exceeds port limit";
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_HW_PW_LIMIT:
+ return "Power denied due to Hardware power limit";
+ default:
+ return "unsupported";
+ }
+}
+
+static const char *c33_pse_ext_substate_short_detected_name(u32 val)
+{
+ switch (val) {
+ case ETHTOOL_C33_PSE_EXT_SUBSTATE_SHORT_DETECTED_SHORT_CONDITION:
+ return "Short condition was detected";
+ default:
+ return "unsupported";
+ }
+}
+
+struct c33_pse_ext_substate_desc {
+ u32 state;
+ const char *(*substate_name)(u32 val);
+};
+
+static const struct c33_pse_ext_substate_desc c33_pse_ext_substate_map[] = {
+ { ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION,
+ c33_pse_ext_substate_error_condition_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID,
+ c33_pse_ext_substate_mr_mps_valid_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE,
+ c33_pse_ext_substate_mr_pse_enable_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED,
+ c33_pse_ext_substate_option_detect_ted_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM,
+ c33_pse_ext_substate_option_vport_lim_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED,
+ c33_pse_ext_substate_ovld_detected_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE,
+ c33_pse_ext_substate_power_not_available_name },
+ { ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED,
+ c33_pse_ext_substate_short_detected_name },
+ { /* sentinel */ }
+};
+
+static void c33_pse_print_ext_substate(u32 state, u32 substate)
+{
+ const struct c33_pse_ext_substate_desc *substate_map;
+
+ substate_map = c33_pse_ext_substate_map;
+ while (substate_map->state) {
+ if (substate_map->state == state) {
+ print_string(PRINT_ANY, "c33-pse-extended-substate",
+ "Clause 33 PSE Extended Substate: %s\n",
+ substate_map->substate_name(substate));
+ return;
+ }
+ substate_map++;
+ }
+}
+
+static int c33_pse_dump_pw_limit_range(const struct nlattr *range)
+{
+ const struct nlattr *range_tb[ETHTOOL_A_C33_PSE_PW_LIMIT_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(range_tb);
+ const struct nlattr *attr;
+ u32 min, max;
+ int ret;
+
+ ret = mnl_attr_parse_nested(range, attr_cb, &range_tb_info);
+ if (ret < 0) {
+ fprintf(stderr,
+ "malformed netlink message (power limit range)\n");
+ return 1;
+ }
+
+ attr = range_tb[ETHTOOL_A_C33_PSE_PW_LIMIT_MIN];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ fprintf(stderr,
+ "malformed netlink message (power limit min)\n");
+ return 1;
+ }
+ min = mnl_attr_get_u32(attr);
+
+ attr = range_tb[ETHTOOL_A_C33_PSE_PW_LIMIT_MAX];
+ if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ fprintf(stderr,
+ "malformed netlink message (power limit max)\n");
+ return 1;
+ }
+ max = mnl_attr_get_u32(attr);
+
+ print_string(PRINT_ANY, "range", "\trange:\n", NULL);
+ print_uint(PRINT_ANY, "min", "\t\tmin %u\n", min);
+ print_uint(PRINT_ANY, "max", "\t\tmax %u\n", max);
+ return 0;
+}
+
int pse_reply_cb(const struct nlmsghdr *nlhdr, void *data)
{
const struct nlattr *tb[ETHTOOL_A_PSE_MAX + 1] = {};
struct nl_context *nlctx = data;
+ const struct nlattr *attr;
DECLARE_ATTR_TB_INFO(tb);
bool silent;
int err_ret;
@@ -151,6 +367,59 @@ int pse_reply_cb(const struct nlmsghdr *nlhdr, void *data)
c33_pse_pw_d_status_name(val));
}

+ if (tb[ETHTOOL_A_C33_PSE_EXT_STATE]) {
+ u32 val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_EXT_STATE]);
+ print_string(PRINT_ANY, "c33-pse-extended-state",
+ "Clause 33 PSE Extended State: %s\n",
+ c33_pse_ext_state_name(val));
+
+ if (tb[ETHTOOL_A_C33_PSE_EXT_SUBSTATE]) {
+ u32 substate;
+
+ substate = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_EXT_SUBSTATE]);
+ c33_pse_print_ext_substate(val, substate);
+ }
+ }
+
+ if (tb[ETHTOOL_A_C33_PSE_PW_CLASS]) {
+ u32 val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_PW_CLASS]);
+ print_uint(PRINT_ANY, "c33-pse-power-class",
+ "Clause 33 PSE Power Class: %u\n", val);
+ }
+
+ if (tb[ETHTOOL_A_C33_PSE_ACTUAL_PW]) {
+ u32 val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_ACTUAL_PW]);
+ print_uint(PRINT_ANY, "c33-pse-actual-power",
+ "Clause 33 PSE Actual Power: %u\n", val);
+ }
+
+ if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) {
+ u32 val;
+
+ val = mnl_attr_get_u32(tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]);
+ print_uint(PRINT_ANY, "c33-pse-available-power-limit",
+ "Clause 33 PSE Available Power Limit: %u\n", val);
+ }
+
+ if (tb[ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES]) {
+ print_string(PRINT_ANY, "c33-pse-power-limit-ranges",
+ "Clause 33 PSE Power Limit Ranges:\n", NULL);
+ mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
+ if (mnl_attr_get_type(attr) == ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES) {
+ if (c33_pse_dump_pw_limit_range(attr)) {
+ close_json_object();
+ return err_ret;
+ }
+ }
+ }
+ }
+
close_json_object();

return MNL_CB_OK;
@@ -212,6 +481,12 @@ static const struct param_parser spse_params[] = {
.handler_data = c33_pse_admin_control_values,
.min_argc = 1,
},
+ {
+ .arg = "c33-pse-avail-pw-limit",
+ .type = ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
{}
};


--
2.34.1