meta-hgxr: nsmd/libnsm TAV Mode Index 20 binding Add libnsm NSM Type 5 Device Mode Index 20 (TAV Mode) codec support and the nsmd QTM4 handler/D-Bus binding, structurally mirroring the merged Power Capping Index 27 implementation. Byte layout (enum8: Default=0/Enabled=1/Disabled=2, 1-byte payload) mirrors Index 27 structurally and by value assignment. No written NSM Index 20 contract exists yet -- this is a best-effort mirror per engineer instruction and must be verified against a written NSM API contract before this ships. Changes: - libnsm/device-configuration.h: DEVICE_MODE_TAV = 20, nsm_tav_mode enum, TAV_MODE_DATA_SIZE, with caveat comments. - libnsm/test/libnsm_device_configuration_test.cpp: tavModeV2 suite mirroring powerCappingModeV2. - nsmd/nsmDeviceInventory/nsmSwitch.hpp/.cpp: NsmSwitchTAVMode sensor class, toTAVModeFromGet, genRequestMsg, handleResponseMsg, setTAVMode, createNsmSwitchTAVMode, gated on entity-manager SupportTAVMode in createNsmSwitchDI. - nsmd/nsmDeviceInventory/test/nsmSwitchFactoryBranch_test.cpp: TAVMode test block mirroring PowerCappingMode coverage. - nsmtool/nsm_config_cmd.cpp: GetTAVMode/SetTAVMode subcommands. Depends on PDI DGXOPENBMC-28636 (com.nvidia.DeviceMode.TAVMode) for the generated server.hpp header used by nsmSwitch.hpp. Review fixups applied: - Removed internal ticket-ID references and "mirrors power capping" style comments from public source/header/test comments (kept the technical rationale). - SupportTAVMode gate now reads via the existing dbusPropertyMapAsBool helper instead of a raw std::get_if<bool>, so an integer-encoded EM value is normalized instead of silently resolving to false. - createNsmSwitchTAVMode kept inline (not static), matching the established file-local factory-helper convention used by createNsmSwitchPowerCappingMode and createNsmSwitchL1PredictionMode in this file. - GetTAVMode::parseResponseMsg rejects a missing/short CurrentMode instead of silently omitting it, matching the GetLTXMode/GetUPhyMode mandatory-CurrentMode convention. - Do not resolve Default on D-Bus publish: nsmd does not resolve a wire Default (0) CurrentMode/PendingMode to a guessed Enabled before publishing to D-Bus, on either the GET path or after a successful reset-to-default Set. No written contract or arch sign-off confirms what TAV's default actually is; the design doc only says it comes from QM4's own INI configuration, and the identical guess-on- mirror pattern was already wrong once for UPhy mode (commit c7c3b2b4). nsmd now publishes the wire value as-is; bmcweb's TAVMode handlers already treat a still-Default CurrentMode/PendingMode as not-yet-resolved by firmware, matching the existing LTXMode/UPhyRecoveryMode contract. Note: the nsmtool GetTAVMode CLI display intentionally still prints raw "Default" for wire value 0 (not "Enabled"), matching the established GetPowerCappingMode/GetLTXMode/ GetUPhyMode CLI precedent in this file. A prior review comment suggested mapping Default to Enabled in the CLI to match D-Bus read semantics, but that no longer applies now that nsmd does not resolve Default to Enabled on the D-Bus side either -- see the note above. Flagging for tech-lead call rather than reintroducing the guess. Test status: local build blocked in this environment by a missing dbus-1 pkg-config dependency (nsmtool/meson.build); clang-format-20 clean on all changed files. No lab/unit run performed -- pending CI and PDI MR merge. Fixes jira https://jirasw.nvidia.com/browse/DGXOPENBMC-28638 Signed-off-by: Surya Garimella <sgarimella@nvidia.com>
diff --git a/libnsm/device-configuration.h b/libnsm/device-configuration.h index 8d23042..d7956fd 100644 --- a/libnsm/device-configuration.h +++ b/libnsm/device-configuration.h
@@ -605,6 +605,7 @@ DEVICE_MODE_PERSISTENT_CPU_POWER_LIMIT_GPU_COPY = 14, DEVICE_MODE_ONE_SHOT_GPU_COPY_SWITCH_POWER_LIMIT = 15, DEVICE_MODE_PERSISTENT_GPU_COPY_SWITCH_POWER_LIMIT = 16, + DEVICE_MODE_TAV = 20, DEVICE_MODE_ADAPTIVE_TGPMODE = 21, DEVICE_MODE_LLDP = 24, DEVICE_MODE_PROTECTION_OPTIONS_MODE = 26, @@ -640,6 +641,18 @@ /** @brief Data size (in bytes) of the Power Capping Mode (index 27) payload. */ #define POWER_CAPPING_MODE_DATA_SIZE 1 +/** @brief TAV (Temperature Aware Voltage) Mode values per NSM Type 5 Device + * Mode Index 20 (DEVICE_MODE_TAV). Single enum8 data byte. + */ +enum nsm_tav_mode { + NSM_TAV_MODE_DEFAULT = 0, + NSM_TAV_MODE_ENABLED = 1, + NSM_TAV_MODE_DISABLED = 2, +}; + +/** @brief Data size (in bytes) of the TAV Mode (index 20) payload. */ +#define TAV_MODE_DATA_SIZE 1 + /** @brief LTX (Link Training Extended) Mode values per NSM Type 5 Device Mode * Index 29 (DEVICE_MODE_LTX). Single enum8 data byte. Non-volatile; * requires a link toggle to take effect.
diff --git a/libnsm/test/libnsm_device_configuration_test.cpp b/libnsm/test/libnsm_device_configuration_test.cpp index 781fa89..394595b 100644 --- a/libnsm/test/libnsm_device_configuration_test.cpp +++ b/libnsm/test/libnsm_device_configuration_test.cpp
@@ -4092,3 +4092,161 @@ ¤t_len, &pending_mode, &pending_len); EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH); } + +// --------------------------------------------------------------------------- +// TAV (Temperature Aware Voltage) Mode (NSM Type 5 Device Mode Index 20, +// enum8). enum8 {0 Default, 1 Enabled, 2 Disabled}; 1-byte data via the +// generic v2 codec. +// --------------------------------------------------------------------------- + +TEST(tavModeV2, enumValues) +{ + EXPECT_EQ(DEVICE_MODE_TAV, 20); + EXPECT_EQ(NSM_TAV_MODE_DEFAULT, 0); + EXPECT_EQ(NSM_TAV_MODE_ENABLED, 1); + EXPECT_EQ(NSM_TAV_MODE_DISABLED, 2); + EXPECT_EQ(TAV_MODE_DATA_SIZE, 1); +} + +TEST(tavModeV2, setEncodeRequestEnabled) +{ + uint32_t device_mode_index = DEVICE_MODE_TAV; + uint8_t mode = NSM_TAV_MODE_ENABLED; + + std::vector<uint8_t> requestMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_set_device_mode_settings_v2_req) + + TAV_MODE_DATA_SIZE - 1, + 0); + auto request = reinterpret_cast<nsm_msg *>(requestMsg.data()); + + auto rc = encode_set_device_mode_settings_v2_req( + 0, device_mode_index, &mode, TAV_MODE_DATA_SIZE, request); + + struct nsm_set_device_mode_settings_v2_req *req = + reinterpret_cast<struct nsm_set_device_mode_settings_v2_req *>( + request->payload); + + EXPECT_EQ(rc, NSM_SW_SUCCESS); + EXPECT_EQ(NSM_SET_DEVICE_MODE_SETTINGS_V2, req->hdr.command); + EXPECT_EQ(sizeof(req->device_mode_index) + TAV_MODE_DATA_SIZE, + req->hdr.data_size); + EXPECT_EQ(device_mode_index, le32toh(req->device_mode_index)); +} + +TEST(tavModeV2, setDecodeRequestDisabled) +{ + uint32_t device_mode_index = DEVICE_MODE_TAV; + uint8_t mode = NSM_TAV_MODE_DISABLED; + + std::vector<uint8_t> requestMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_set_device_mode_settings_v2_req) + + TAV_MODE_DATA_SIZE - 1, + 0); + auto request = reinterpret_cast<nsm_msg *>(requestMsg.data()); + + encode_set_device_mode_settings_v2_req(0, device_mode_index, &mode, + TAV_MODE_DATA_SIZE, request); + + uint32_t decoded_index = 0; + uint8_t decoded_data[TAV_MODE_DATA_SIZE] = {0}; + uint16_t decoded_data_length = 0; + + auto rc = decode_set_device_mode_settings_v2_req( + request, requestMsg.size(), &decoded_index, decoded_data, + &decoded_data_length); + + EXPECT_EQ(rc, NSM_SW_SUCCESS); + EXPECT_EQ(device_mode_index, decoded_index); + EXPECT_EQ(TAV_MODE_DATA_SIZE, decoded_data_length); + EXPECT_EQ(NSM_TAV_MODE_DISABLED, decoded_data[0]); +} + +TEST(tavModeV2, getEncodeRequest) +{ + std::vector<uint8_t> requestMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_req), + 0); + auto request = reinterpret_cast<nsm_msg *>(requestMsg.data()); + + auto rc = + encode_get_device_mode_settings_v2_req(0, DEVICE_MODE_TAV, request); + + uint32_t decoded_index = 0; + EXPECT_EQ(rc, NSM_SW_SUCCESS); + EXPECT_EQ(decode_get_device_mode_settings_v2_req( + request, requestMsg.size(), &decoded_index), + NSM_SW_SUCCESS); + EXPECT_EQ(DEVICE_MODE_TAV, decoded_index); +} + +TEST(tavModeV2, supportedListIncludesIndex20) +{ + const uint16_t handle = 0x0000; + const uint16_t mode_count = 2; + const uint32_t mode_list[] = {DEVICE_MODE_LLDP, DEVICE_MODE_TAV}; + + std::vector<uint8_t> responseMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_supported_device_modes_resp) + + (mode_count - 1) * sizeof(uint32_t), + 0); + auto response = reinterpret_cast<nsm_msg *>(responseMsg.data()); + + ASSERT_EQ(encode_get_supported_device_modes_resp( + 0, NSM_SUCCESS, ERR_NULL, handle, mode_count, mode_list, + response), + NSM_SW_SUCCESS); + + uint8_t cc = 0; + uint16_t reason_code = 0; + uint16_t decoded_handle = 0; + uint16_t decoded_mode_count = 0; + uint32_t decoded_mode_list[2] = {0}; + + ASSERT_EQ(decode_get_supported_device_modes_resp( + response, responseMsg.size(), &cc, &reason_code, + &decoded_handle, &decoded_mode_count, decoded_mode_list), + NSM_SW_SUCCESS); + EXPECT_EQ(NSM_SUCCESS, cc); + EXPECT_EQ(mode_count, decoded_mode_count); + + bool found = false; + for (uint16_t i = 0; i < decoded_mode_count; i++) { + if (decoded_mode_list[i] == DEVICE_MODE_TAV) { + found = true; + } + } + EXPECT_TRUE(found); +} + +TEST(tavModeV2, setEncodeRejectsNullDataWithLength) +{ + /* Bounds guard: encode must fail when length > 0 but data pointer is + * null. */ + std::vector<uint8_t> requestMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_set_device_mode_settings_v2_req) + + TAV_MODE_DATA_SIZE - 1, + 0); + auto request = reinterpret_cast<nsm_msg *>(requestMsg.data()); + auto rc = encode_set_device_mode_settings_v2_req( + 0, DEVICE_MODE_TAV, nullptr, TAV_MODE_DATA_SIZE, request); + EXPECT_EQ(rc, NSM_SW_ERROR_NULL); +} + +TEST(tavModeV2, getDecodeRejectsTruncatedResponse) +{ + /* Oversized-buffer / truncated-response regression for the shared v2 + * codec used by index 20: msg_len shorter than the fixed header must + * fail. */ + std::vector<uint8_t> responseMsg(sizeof(nsm_msg_hdr) + 4, 0); + auto response = reinterpret_cast<nsm_msg *>(responseMsg.data()); + uint8_t cc = 0; + uint16_t reason_code = 0; + uint8_t current_mode = 0; + uint8_t pending_mode = 0; + uint16_t current_len = 0; + uint16_t pending_len = 0; + auto rc = decode_get_device_mode_settings_v2_resp( + response, responseMsg.size(), &cc, &reason_code, ¤t_mode, + ¤t_len, &pending_mode, &pending_len); + EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH); +}
diff --git a/nsmd/nsmDeviceInventory/nsmSwitch.cpp b/nsmd/nsmDeviceInventory/nsmSwitch.cpp index 48dc929..d8a31c2 100644 --- a/nsmd/nsmDeviceInventory/nsmSwitch.cpp +++ b/nsmd/nsmDeviceInventory/nsmSwitch.cpp
@@ -963,6 +963,251 @@ nvSwitchPowerCappingMode, device}); } +// Map NSM wire enum8 to TAVMode. Wire Default (0) is published to D-Bus +// as-is; nsmd does not resolve it to a concrete Enabled/Disabled value. +// Only the device knows what its configured default is -- bmcweb already +// treats a still-Default CurrentMode/PendingMode as "not yet resolved by +// firmware" (skips the property on Settings, errors on the active +// resource) rather than guessing, matching LTXMode/UPhyRecoveryMode. +static std::optional<TAVMode> toTAVModeFromGet(uint8_t nsmMode) +{ + switch (nsmMode) + { + case NSM_TAV_MODE_ENABLED: + return TAVMode::Enabled; + case NSM_TAV_MODE_DISABLED: + return TAVMode::Disabled; + case NSM_TAV_MODE_DEFAULT: + return TAVMode::Default; + default: + return std::nullopt; + } +} + +std::optional<std::vector<uint8_t>> + NsmSwitchTAVMode::genRequestMsg(eid_t eid, uint8_t instanceId) +{ + std::vector<uint8_t> request(sizeof(nsm_msg_hdr) + + sizeof(nsm_get_device_mode_settings_v2_req)); + auto requestPtr = reinterpret_cast<struct nsm_msg*>(request.data()); + auto rc = encode_get_device_mode_settings_v2_req( + instanceId, DEVICE_MODE_TAV, requestPtr); + if (rc != NSM_SW_SUCCESS) + { + lg2::debug("encode_get_device_mode_settings_v2_req failed. " + "eid={EID} rc={RC}", + "EID", eid, "RC", rc); + return std::nullopt; + } + return request; +} + +uint8_t NsmSwitchTAVMode::handleResponseMsg(const struct nsm_msg* responseMsg, + size_t responseLen) +{ + uint8_t cc = NSM_ERROR; + uint16_t reason_code = ERR_NULL; + uint8_t currentMode = 0; + uint8_t pendingMode = 0; + uint16_t currentModeLength = 0; + uint16_t pendingModeLength = 0; + + /* Probe lengths with null data pointers so decode cannot overflow the + * 1-byte mode buffers below on a malformed oversized payload. */ + auto rc = decode_get_device_mode_settings_v2_resp( + responseMsg, responseLen, &cc, &reason_code, nullptr, + ¤tModeLength, nullptr, &pendingModeLength); + if (rc != NSM_SW_SUCCESS || cc != NSM_SUCCESS) + { + return cc ? cc : rc; + } + if (currentModeLength != TAV_MODE_DATA_SIZE || + (pendingModeLength != 0 && pendingModeLength != TAV_MODE_DATA_SIZE)) + { + return NSM_SW_ERROR_LENGTH; + } + + rc = decode_get_device_mode_settings_v2_resp( + responseMsg, responseLen, &cc, &reason_code, ¤tMode, + ¤tModeLength, &pendingMode, &pendingModeLength); + + if (rc == NSM_SW_SUCCESS && cc == NSM_SUCCESS) + { + if (currentModeLength != TAV_MODE_DATA_SIZE) + { + return NSM_SW_ERROR_LENGTH; + } + auto current = toTAVModeFromGet(currentMode); + if (!current) + { + return NSM_SW_ERROR_DATA; + } + // Publish the wire value as-is -- nsmd does not decide what + // Default resolves to. bmcweb's active-resource handler already + // treats a still-Default CurrentMode as not-yet-resolved and + // surfaces it as an error rather than accepting a guess. + tavModeIntf->currentMode(*current); + + if (pendingModeLength != 0 && pendingModeLength != TAV_MODE_DATA_SIZE) + { + return NSM_SW_ERROR_LENGTH; + } + if (pendingModeLength == TAV_MODE_DATA_SIZE) + { + auto pending = toTAVModeFromGet(pendingMode); + if (!pending) + { + return NSM_SW_ERROR_DATA; + } + // Same as above: publish as-is. bmcweb's Settings handler + // already skips a still-Default PendingMode until a later + // poll reports the device's resolved value. + tavModeIntf->pendingMode(*pending); + } + else + { + // No pending override from device; keep Settings in sync. + tavModeIntf->pendingMode(*current); + } + } + return cc ? cc : rc; +} + +requester::Coroutine NsmSwitchTAVMode::setTAVMode( + const AsyncSetOperationValueType& value, + [[maybe_unused]] AsyncOperationStatusType* status, + std::shared_ptr<NsmDevice> device) +{ + const std::string* requested = std::get_if<std::string>(&value); + if (!requested) + { + throw sdbusplus::error::xyz::openbmc_project::common::InvalidArgument{}; + } + + auto eid = device->getEid(); + auto mode = TAVModeServer::convertTAVModeValueFromString(*requested); + + uint8_t data = 0; + if (mode == TAVMode::Default) + { + data = NSM_TAV_MODE_DEFAULT; + } + else if (mode == TAVMode::Enabled) + { + data = NSM_TAV_MODE_ENABLED; + } + else if (mode == TAVMode::Disabled) + { + data = NSM_TAV_MODE_DISABLED; + } + else + { + throw sdbusplus::error::xyz::openbmc_project::common::InvalidArgument{}; + } + + if (!tavModeIntf->isModeConfigurable()) + { + *status = AsyncOperationStatusType::Unavailable; + throw sdbusplus::error::xyz::openbmc_project::common::NotAllowed{}; + } + + Request request(sizeof(nsm_msg_hdr) + + sizeof(nsm_set_device_mode_settings_v2_req) + + TAV_MODE_DATA_SIZE - 1); + auto requestMsg = reinterpret_cast<nsm_msg*>(request.data()); + auto rc = encode_set_device_mode_settings_v2_req( + 0, DEVICE_MODE_TAV, &data, TAV_MODE_DATA_SIZE, requestMsg); + if (shouldLog("setTAVMode encode", uint16_t(0), uint8_t(0), rc)) + { + lg2::error("Encoding TAV mode failed. eid={EID} rc={RC}", "EID", eid, + "RC", rc); + } + if (rc) + { + *status = AsyncOperationStatusType::WriteFailure; + co_return NSM_SW_ERROR_COMMAND_FAIL; + } + + std::shared_ptr<const nsm_msg> responseMsg; + size_t responseLen = 0; + auto rc_ = co_await device->postPatchIO(eid, request, responseMsg, + responseLen); + if (shouldLog("setTAVMode postPatchIO", uint16_t(0), uint8_t(0), rc_)) + { + lg2::error("Setting TAV mode failed. eid={EID} rc={RC}", "EID", eid, + "RC", utils::nsmSwCodeToString(rc_)); + } + if (rc_) + { + *status = AsyncOperationStatusType::WriteFailure; + co_return NSM_SW_ERROR_COMMAND_FAIL; + } + + uint8_t cc = NSM_SUCCESS; + uint16_t reason_code = ERR_NULL; + rc = decode_set_device_mode_settings_v2_resp(responseMsg.get(), responseLen, + &cc, &reason_code); + if (shouldLog("setTAVMode response", reason_code, cc, rc)) + { + lg2::error( + "Setting TAV mode returned an error. eid={EID} cc={CC} reasonCode={REASON} rc={RC}", + "EID", eid, "CC", cc, "REASON", reason_code, "RC", rc); + } + if (rc == NSM_SW_SUCCESS && cc == NSM_SUCCESS) + { + // Publish exactly what was requested, including Default -- a + // reset-to-default request is genuinely pending until the device + // reports a resolved value on a later poll. Do not guess Enabled. + tavModeIntf->pendingMode(mode); + } + else + { + *status = AsyncOperationStatusType::WriteFailure; + co_return NSM_SW_ERROR_COMMAND_FAIL; + } + co_return NSM_SW_SUCCESS; +} + +inline void createNsmSwitchTAVMode(std::shared_ptr<NsmDevice> device, + sdbusplus::bus_t& bus, + const std::string& objPath, + const std::string& type, + const std::string& name) +{ + auto dbusObjPath = objPath + name + "/Oem/Nvidia/TAVMode"; + std::vector<utils::Association> associations{ + {"parent_switch", "tav_mode", objPath + name}}; + auto tavModeAssociationIntf = + std::make_unique<AssociationDefinitionsInft>(bus, dbusObjPath.c_str()); + std::vector<std::tuple<std::string, std::string, std::string>> + associationsList; + for (const auto& association : associations) + { + associationsList.emplace_back(association.forward, association.backward, + association.absolutePath); + } + tavModeAssociationIntf->associations(associationsList); + + auto tavModeIntf = std::make_shared<TAVModeIntf>(bus, dbusObjPath.c_str()); + tavModeIntf->currentMode(TAVMode::Enabled); + tavModeIntf->pendingMode(TAVMode::Enabled); + // Configurability is sourced solely from the SupportTAVMode + // entity-manager capability flag via the createNsmSwitchDI gate below. + tavModeIntf->isModeConfigurable(true); + auto nvSwitchTAVMode = std::make_shared<NsmSwitchTAVMode>( + name, type, tavModeIntf, std::move(tavModeAssociationIntf)); + device->addSensor(nvSwitchTAVMode, false); + + nsm::AsyncSetOperationHandler setTAVModeHandler = std::bind( + &NsmSwitchTAVMode::setTAVMode, nvSwitchTAVMode, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + AsyncOperationManager::getInstance() + ->getDispatcher(dbusObjPath) + ->addAsyncSetOperation( + std::string(TAVModeServer::interface), "PendingMode", + AsyncSetOperationInfo{setTAVModeHandler, nvSwitchTAVMode, device}); +} + // Map NSM wire enum8 to LTXModeEnum. Wire Default (0) is resolved to Enabled // at D-Bus publish time per the LTX mode contract. static std::optional<LTXModeEnum> toLTXModeFromGet(uint8_t nsmMode) @@ -1580,6 +1825,16 @@ name); } + // An absent support property means TAV mode is not exposed. Read it + // from the cached base properties so absence needs no D-Bus fetch. + // dbusPropertyMapAsBool also normalizes an integer-encoded EM value. + const bool supportTAVMode = + dbusPropertyMapAsBool(allBaseIfaceProperties, "SupportTAVMode"); + if (supportTAVMode) + { + createNsmSwitchTAVMode(device, bus, inventoryObjPath, type, name); + } + bool supportLTXMode = false; auto ltxModeProperty = allBaseIfaceProperties.find("SupportLTXMode"); if (ltxModeProperty != allBaseIfaceProperties.end())
diff --git a/nsmd/nsmDeviceInventory/nsmSwitch.hpp b/nsmd/nsmDeviceInventory/nsmSwitch.hpp index 18c869b..42281cd 100644 --- a/nsmd/nsmDeviceInventory/nsmSwitch.hpp +++ b/nsmd/nsmDeviceInventory/nsmSwitch.hpp
@@ -14,6 +14,7 @@ #include <com/nvidia/DeviceMode/LTXMode/server.hpp> #include <com/nvidia/DeviceMode/PowerCappingMode/server.hpp> +#include <com/nvidia/DeviceMode/TAVMode/server.hpp> #include <com/nvidia/DeviceMode/UPhyRecoveryMode/server.hpp> #include <com/nvidia/PowerMode/server.hpp> #include <com/nvidia/SwitchIsolation/server.hpp> @@ -48,6 +49,13 @@ using LTXModeServer = sdbusplus::com::nvidia::DeviceMode::server::LTXMode; using LTXModeIntf = object_t<LTXModeServer>; using LTXModeEnum = LTXModeServer::LinkTrainingExtendedMode; +// TAVMode PDI shape: CurrentMode/PendingMode/IsModeConfigurable, +// TAVModeValue{Default,Enabled,Disabled}. Gated on the PDI +// com.nvidia.DeviceMode.TAVMode interface merging so this +// generated header exists to compile against. +using TAVModeServer = sdbusplus::com::nvidia::DeviceMode::server::TAVMode; +using TAVModeIntf = object_t<TAVModeServer>; +using TAVMode = TAVModeServer::TAVModeValue; using UPhyModeServer = sdbusplus::com::nvidia::DeviceMode::server::UPhyRecoveryMode; using UPhyModeIntf = object_t<UPhyModeServer>; @@ -196,6 +204,37 @@ std::shared_ptr<AssociationDefinitionsInft> associationDefIntf; }; +/** @brief TAV (Temperature Aware Voltage) mode using NSM Type 5 Device Mode + * index 20 (DEVICE_MODE_TAV). + * + * enum8 {0 Default, 1 Enabled, 2 Disabled}; 1-byte payload via the + * generic v2 codec. */ +class NsmSwitchTAVMode : public NsmSensor +{ + public: + NsmSwitchTAVMode( + const std::string& name, const std::string& type, + std::shared_ptr<TAVModeIntf> tavModeIntf, + std::shared_ptr<AssociationDefinitionsInft> associationDefIntf) : + NsmSensor(name, type), tavModeIntf(tavModeIntf), + associationDefIntf(associationDefIntf) + {} + + std::optional<std::vector<uint8_t>> + genRequestMsg(eid_t eid, uint8_t instanceId) override; + uint8_t handleResponseMsg(const struct nsm_msg* responseMsg, + size_t responseLen) override; + + requester::Coroutine + setTAVMode(const AsyncSetOperationValueType& value, + [[maybe_unused]] AsyncOperationStatusType* status, + std::shared_ptr<NsmDevice> device); + + private: + std::shared_ptr<TAVModeIntf> tavModeIntf; + std::shared_ptr<AssociationDefinitionsInft> associationDefIntf; +}; + /** @brief LTX (Link Training Extended) mode using NSM Type 5 Device Mode * index 29 (DEVICE_MODE_LTX). */ class NsmSwitchLTXMode : public NsmSensor
diff --git a/nsmd/nsmDeviceInventory/test/nsmSwitchFactoryBranch_test.cpp b/nsmd/nsmDeviceInventory/test/nsmSwitchFactoryBranch_test.cpp index cda22b3..ace8a8f 100644 --- a/nsmd/nsmDeviceInventory/test/nsmSwitchFactoryBranch_test.cpp +++ b/nsmd/nsmDeviceInventory/test/nsmSwitchFactoryBranch_test.cpp
@@ -1234,3 +1234,271 @@ rc = sensor.handleResponseMsg(response, responseMsg.size()); EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH); } + +// ============================================================================ +// NsmSwitchTAVMode -- NSM Type 5 Device Mode index 20 coverage. +// ============================================================================ + +TEST_F(NsmSwitchFactoryBranchTest, TAVMode_GenRequestMsg_EncodeFail) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_genreq_fail"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_genreq_fail"); + NsmSwitchTAVMode sensor("Tav_genreq", "NSM_NVSwitch", tavIntf, assocIntf); + + auto request = sensor.genRequestMsg(12, NSM_INSTANCE_MAX + 1); + EXPECT_FALSE(request.has_value()); +} + +TEST_F(NsmSwitchFactoryBranchTest, TAVMode_HandleResponse_AllModes) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_all_modes"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_all_modes"); + NsmSwitchTAVMode sensor("Tav_modes", "NSM_NVSwitch", tavIntf, assocIntf); + + std::vector<uint8_t> responseMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_resp) + + TAV_MODE_DATA_SIZE * 2, + 0); + auto response = reinterpret_cast<nsm_msg*>(responseMsg.data()); + + uint8_t currentMode = NSM_TAV_MODE_ENABLED; + uint8_t pendingMode = NSM_TAV_MODE_DISABLED; + auto rc = encode_get_device_mode_settings_v2_resp( + 0, NSM_SUCCESS, ERR_NULL, ¤tMode, TAV_MODE_DATA_SIZE, + &pendingMode, TAV_MODE_DATA_SIZE, response); + ASSERT_EQ(rc, NSM_SW_SUCCESS); + + rc = sensor.handleResponseMsg(response, responseMsg.size()); + EXPECT_EQ(rc, NSM_SW_SUCCESS); + EXPECT_EQ(tavIntf->currentMode(), TAVMode::Enabled); + EXPECT_EQ(tavIntf->pendingMode(), TAVMode::Disabled); +} + +TEST_F(NsmSwitchFactoryBranchTest, + TAVMode_HandleResponse_DefaultWirePassedThrough) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_default_wire"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_default_wire"); + NsmSwitchTAVMode sensor("Tav_defwire", "NSM_NVSwitch", tavIntf, assocIntf); + + std::vector<uint8_t> responseMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_resp) + + TAV_MODE_DATA_SIZE * 2, + 0); + auto response = reinterpret_cast<nsm_msg*>(responseMsg.data()); + + uint8_t currentMode = NSM_TAV_MODE_DEFAULT; + uint8_t pendingMode = NSM_TAV_MODE_DEFAULT; + auto rc = encode_get_device_mode_settings_v2_resp( + 0, NSM_SUCCESS, ERR_NULL, ¤tMode, TAV_MODE_DATA_SIZE, + &pendingMode, TAV_MODE_DATA_SIZE, response); + ASSERT_EQ(rc, NSM_SW_SUCCESS); + + rc = sensor.handleResponseMsg(response, responseMsg.size()); + EXPECT_EQ(rc, NSM_SW_SUCCESS); + // nsmd never resolves Default -- it publishes the wire value as-is and + // leaves resolution to the device / to bmcweb's not-yet-resolved + // handling (skip on Settings, error on the active resource). + EXPECT_EQ(tavIntf->currentMode(), TAVMode::Default); + EXPECT_EQ(tavIntf->pendingMode(), TAVMode::Default); +} + +TEST_F(NsmSwitchFactoryBranchTest, TAVMode_HandleResponse_InvalidMode) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_invalid_mode"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_invalid_mode"); + NsmSwitchTAVMode sensor("Tav_invalid", "NSM_NVSwitch", tavIntf, assocIntf); + + std::vector<uint8_t> responseMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_resp) + + TAV_MODE_DATA_SIZE, + 0); + auto response = reinterpret_cast<nsm_msg*>(responseMsg.data()); + + uint8_t currentMode = 99; + auto rc = encode_get_device_mode_settings_v2_resp( + 0, NSM_SUCCESS, ERR_NULL, ¤tMode, TAV_MODE_DATA_SIZE, nullptr, 0, + response); + ASSERT_EQ(rc, NSM_SW_SUCCESS); + + rc = sensor.handleResponseMsg(response, responseMsg.size()); + EXPECT_EQ(rc, NSM_SW_ERROR_DATA); +} + +TEST_F(NsmSwitchFactoryBranchTest, SetTAVMode_NotConfigurable_NotAllowed) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_not_cfg"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_not_cfg"); + tavIntf->isModeConfigurable(false); + NsmSwitchTAVMode sensor("Tav_nocfg", "NSM_NVSwitch", tavIntf, assocIntf); + + AsyncOperationStatusType status = AsyncOperationStatusType::Success; + AsyncSetOperationValueType value = + TAVModeServer::convertTAVModeValueToString(TAVMode::Default); + + EXPECT_THROW_COROUTINE( + sensor.setTAVMode(value, &status, nvswitch), + sdbusplus::error::xyz::openbmc_project::common::NotAllowed); + EXPECT_EQ(status, AsyncOperationStatusType::Unavailable); +} + +TEST_F(NsmSwitchFactoryBranchTest, SetTAVMode_Default_Success) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_set_default"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_set_default"); + // The factory publishes IsModeConfigurable true; the PDI default is false. + tavIntf->isModeConfigurable(true); + NsmSwitchTAVMode sensor("Tav_setdef", "NSM_NVSwitch", tavIntf, assocIntf); + + AsyncOperationStatusType status = AsyncOperationStatusType::Success; + AsyncSetOperationValueType value = + TAVModeServer::convertTAVModeValueToString(TAVMode::Default); + + std::vector<uint8_t> responseData( + sizeof(nsm_msg_hdr) + sizeof(nsm_common_resp), 0); + auto* responseMsg = reinterpret_cast<nsm_msg*>(responseData.data()); + encode_set_device_mode_settings_v2_resp(0, NSM_SUCCESS, ERR_NULL, + responseMsg); + + EXPECT_CALL(*nvswitch, postPatchIO(_, _, _, _)) + .WillOnce(mockPostPatchIO(responseData)); + + sensor.setTAVMode(value, &status, nvswitch); + + EXPECT_EQ(status, AsyncOperationStatusType::Success); + // A reset-to-default request publishes Default on PendingMode -- it is + // genuinely pending until the device reports a resolved value on a + // later poll; nsmd does not guess Enabled. + EXPECT_EQ(tavIntf->pendingMode(), TAVMode::Default); +} + +// ============================================================================ +// createNsmSwitchDI gate: SupportTAVMode true/false +// ============================================================================ + +static size_t countTAVModeSensors(const std::shared_ptr<MockNsmDevice>& dev) +{ + size_t count = 0; + for (const auto& sensor : dev->roundRobinSensors) + { + if (std::dynamic_pointer_cast<NsmSwitchTAVMode>(sensor)) + { + ++count; + } + } + return count; +} + +TEST_F(NsmSwitchFactoryBranchTest, Factory_NVSwitch_SupportTAVModeTrue) +{ + const std::string path = inventoryPath + "nvs_tav_true"; + setupBaseProperties(path, {{"Type", std::string("NSM_NVSwitch")}, + {"SupportL1PredictionMode", bool(false)}, + {"SupportTAVMode", bool(true)}}); + + ASSERT_EQ(countTAVModeSensors(nvswitch), 0u); + createNsmSwitchDI(mockManager, baseIntf, path); + EXPECT_EQ(countTAVModeSensors(nvswitch), 1u); +} + +TEST_F(NsmSwitchFactoryBranchTest, Factory_NVSwitch_SupportTAVModeFalse) +{ + const std::string path = inventoryPath + "nvs_tav_false"; + setupBaseProperties(path, {{"Type", std::string("NSM_NVSwitch")}, + {"SupportL1PredictionMode", bool(false)}, + {"SupportTAVMode", bool(false)}}); + + const size_t before = nvswitch->roundRobinSensors.size(); + createNsmSwitchDI(mockManager, baseIntf, path); + // Gate closed for TAV mode, other NVSwitch sensors still created. + EXPECT_EQ(countTAVModeSensors(nvswitch), 0u); + EXPECT_GT(nvswitch->roundRobinSensors.size(), before); +} + +TEST_F(NsmSwitchFactoryBranchTest, Factory_NVSwitch_SupportTAVModeAbsent) +{ + const std::string path = inventoryPath + "nvs_tav_absent"; + // SupportTAVMode omitted entirely from the base properties. + setupBaseProperties(path, {{"Type", std::string("NSM_NVSwitch")}, + {"SupportL1PredictionMode", bool(false)}}); + + const size_t before = nvswitch->roundRobinSensors.size(); + createNsmSwitchDI(mockManager, baseIntf, path); + EXPECT_EQ(countTAVModeSensors(nvswitch), 0u); + EXPECT_GT(nvswitch->roundRobinSensors.size(), before); +} + +TEST_F(NsmSwitchFactoryBranchTest, TAVMode_HandleResponse_NoPendingSyncsPending) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_no_pending"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_no_pending"); + // Stale pending from a prior write / factory seed. + tavIntf->pendingMode(TAVMode::Disabled); + NsmSwitchTAVMode sensor("Tav_nopending", "NSM_NVSwitch", tavIntf, + assocIntf); + + std::vector<uint8_t> responseMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_resp) + + TAV_MODE_DATA_SIZE, + 0); + auto response = reinterpret_cast<nsm_msg*>(responseMsg.data()); + + uint8_t currentMode = NSM_TAV_MODE_ENABLED; + auto rc = encode_get_device_mode_settings_v2_resp( + 0, NSM_SUCCESS, ERR_NULL, ¤tMode, TAV_MODE_DATA_SIZE, nullptr, 0, + response); + ASSERT_EQ(rc, NSM_SW_SUCCESS); + + rc = sensor.handleResponseMsg(response, responseMsg.size()); + EXPECT_EQ(rc, NSM_SW_SUCCESS); + EXPECT_EQ(tavIntf->currentMode(), TAVMode::Enabled); + EXPECT_EQ(tavIntf->pendingMode(), TAVMode::Enabled); +} + +TEST_F(NsmSwitchFactoryBranchTest, TAVMode_HandleResponse_BadLength) +{ + static auto& testBus = utils::DBusHandler::getBus(); + auto tavIntf = std::make_shared<TAVModeIntf>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_bad_len"); + auto assocIntf = std::make_shared<AssociationDefinitionsInft>( + testBus, "/xyz/openbmc_project/inventory/fabr/tav_bad_len"); + NsmSwitchTAVMode sensor("Tav_badlen", "NSM_NVSwitch", tavIntf, assocIntf); + + std::vector<uint8_t> responseMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_resp) + 4, + 0); + auto response = reinterpret_cast<nsm_msg*>(responseMsg.data()); + + // Two source bytes so the encoder stays in bounds while still emitting a + // 2-byte current payload, which is malformed for enum8 index 20. + uint8_t currentMode[2] = {NSM_TAV_MODE_ENABLED, 0}; + uint8_t pendingMode = NSM_TAV_MODE_DISABLED; + auto rc = encode_get_device_mode_settings_v2_resp( + 0, NSM_SUCCESS, ERR_NULL, currentMode, uint16_t(sizeof(currentMode)), + &pendingMode, TAV_MODE_DATA_SIZE, response); + ASSERT_EQ(rc, NSM_SW_SUCCESS); + + rc = sensor.handleResponseMsg(response, responseMsg.size()); + EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH); +}
diff --git a/nsmtool/nsm_config_cmd.cpp b/nsmtool/nsm_config_cmd.cpp index 378641e..994bb68 100644 --- a/nsmtool/nsm_config_cmd.cpp +++ b/nsmtool/nsm_config_cmd.cpp
@@ -2258,6 +2258,195 @@ }; /** + * Human-readable NSM Type 5 Device Mode Index 20 (TAV Mode) enum8. + */ +namespace +{ +inline std::string tavModeFromGetWireToString(uint8_t value) +{ + switch (value) + { + case NSM_TAV_MODE_ENABLED: + return "Enabled"; + case NSM_TAV_MODE_DISABLED: + return "Disabled"; + case NSM_TAV_MODE_DEFAULT: + return "Default"; + default: + return "Unknown(" + std::to_string(static_cast<int>(value)) + ")"; + } +} +} // namespace + +/** + * config subcommands targeting NSM Type 5 DEVICE_MODE_TAV (index 20) + * so the TAV mode (NSM Type 5 idx 20) enable/disable mode can be read/set + * without hand-rolling a raw Get/Set Device Mode Settings v2 command. + */ +class GetTAVMode : public CommandInterface +{ + public: + ~GetTAVMode() = default; + GetTAVMode() = delete; + GetTAVMode(const GetTAVMode&) = delete; + GetTAVMode(GetTAVMode&&) = default; + GetTAVMode& operator=(const GetTAVMode&) = delete; + GetTAVMode& operator=(GetTAVMode&&) = default; + + using CommandInterface::CommandInterface; + + explicit GetTAVMode(const char* type, const char* name, CLI::App* app) : + CommandInterface(type, name, app) + {} + + std::pair<int, std::vector<uint8_t>> createRequestMsg() override + { + std::vector<uint8_t> requestMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_get_device_mode_settings_v2_req), + 0); + auto request = reinterpret_cast<nsm_msg*>(requestMsg.data()); + auto rc = encode_get_device_mode_settings_v2_req( + instanceId, static_cast<uint32_t>(DEVICE_MODE_TAV), request); + return {rc, requestMsg}; + } + + void parseResponseMsg(nsm_msg* responsePtr, size_t payloadLength) override + { + uint8_t cc = NSM_ERROR; + uint16_t reasonCode = ERR_NULL; + uint8_t currentData[TAV_MODE_DATA_SIZE] = {}; + uint8_t pendingData[TAV_MODE_DATA_SIZE] = {}; + uint16_t currentLength = 0; + uint16_t pendingLength = 0; + + /* Probe lengths first: reject oversized mode payloads before decode + * copies into the fixed TAV_MODE_DATA_SIZE stack buffers. */ + auto rc = decode_get_device_mode_settings_v2_resp( + responsePtr, payloadLength, &cc, &reasonCode, nullptr, + ¤tLength, nullptr, &pendingLength); + if (rc != NSM_SW_SUCCESS || cc != NSM_SUCCESS) + { + std::cerr << "Response message error: rc=" << rc + << ", cc=" << static_cast<int>(cc) + << ", reasonCode=" << static_cast<int>(reasonCode) + << "\n"; + return; + } + if (currentLength != TAV_MODE_DATA_SIZE || + (pendingLength != 0 && pendingLength != TAV_MODE_DATA_SIZE)) + { + std::cerr << "TAV mode payload invalid: current=" << currentLength + << " pending=" << pendingLength + << " (expected current==" << TAV_MODE_DATA_SIZE + << ", pending==0 or " << TAV_MODE_DATA_SIZE << ")\n"; + return; + } + rc = decode_get_device_mode_settings_v2_resp( + responsePtr, payloadLength, &cc, &reasonCode, currentData, + ¤tLength, pendingData, &pendingLength); + if (rc != NSM_SW_SUCCESS || cc != NSM_SUCCESS) + { + std::cerr << "Response message error: rc=" << rc + << ", cc=" << static_cast<int>(cc) + << ", reasonCode=" << static_cast<int>(reasonCode) + << "\n"; + return; + } + ordered_json result; + result["Completion Code"] = cc; + result["CurrentMode"] = tavModeFromGetWireToString(currentData[0]); + if (pendingLength == TAV_MODE_DATA_SIZE) + { + result["PendingMode"] = tavModeFromGetWireToString(pendingData[0]); + result["ResetRequired"] = (pendingData[0] != currentData[0]); + } + nsmtool::helper::DisplayInJson(result); + } +}; + +class SetTAVMode : public CommandInterface +{ + public: + ~SetTAVMode() = default; + SetTAVMode() = delete; + SetTAVMode(const SetTAVMode&) = delete; + SetTAVMode(SetTAVMode&&) = default; + SetTAVMode& operator=(const SetTAVMode&) = delete; + SetTAVMode& operator=(SetTAVMode&&) = default; + + using CommandInterface::CommandInterface; + + explicit SetTAVMode(const char* type, const char* name, CLI::App* app) : + CommandInterface(type, name, app) + { + auto g = app->add_option_group( + "Required", + "Set TAV Mode (NSM Type 5 idx 20): Default | Enabled | Disabled"); + g->add_option("-M, --mode", modeStr, + "TAV mode: Default, Enabled, or Disabled"); + g->require_option(1); + } + + std::pair<int, std::vector<uint8_t>> createRequestMsg() override + { + uint8_t mode = 0; + std::string m = modeStr; + std::transform(m.begin(), m.end(), m.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (m == "default" || m == "0") + { + mode = NSM_TAV_MODE_DEFAULT; + } + else if (m == "enabled" || m == "enable" || m == "1") + { + mode = NSM_TAV_MODE_ENABLED; + } + else if (m == "disabled" || m == "disable" || m == "2") + { + mode = NSM_TAV_MODE_DISABLED; + } + else + { + std::cerr << "Invalid mode '" << modeStr + << "' (expected Default, Enabled, or Disabled)\n"; + return {NSM_SW_ERROR_DATA, {}}; + } + + std::vector<uint8_t> requestMsg( + sizeof(nsm_msg_hdr) + sizeof(nsm_set_device_mode_settings_v2_req) + + TAV_MODE_DATA_SIZE - 1, + 0); + auto request = reinterpret_cast<nsm_msg*>(requestMsg.data()); + auto rc = encode_set_device_mode_settings_v2_req( + instanceId, static_cast<uint32_t>(DEVICE_MODE_TAV), &mode, + TAV_MODE_DATA_SIZE, request); + return {rc, requestMsg}; + } + + void parseResponseMsg(nsm_msg* responsePtr, size_t payloadLength) override + { + uint8_t cc = NSM_ERROR; + uint16_t reasonCode = ERR_NULL; + auto rc = decode_set_device_mode_settings_v2_resp( + responsePtr, payloadLength, &cc, &reasonCode); + if (rc != NSM_SW_SUCCESS || cc != NSM_SUCCESS) + { + std::cerr << "Response error: rc=" << rc + << ", cc=" << static_cast<int>(cc) + << ", reasonCode=" << static_cast<int>(reasonCode) + << "\n"; + return; + } + ordered_json result; + result["Completion Code"] = cc; + nsmtool::helper::DisplayInJson(result); + } + + private: + std::string modeStr{}; +}; + +/** * config subcommands targeting NSM Type 5 DEVICE_MODE_LTX (index 29) * so the LTX enable/disable mode can be read/set without hand-rolling a raw * Get/Set Device Mode Settings v2 command. @@ -2765,6 +2954,16 @@ commands.push_back(std::make_unique<SetPowerCappingMode>( "config", "SetPowerCappingMode", setPowerCappingMode)); + auto getTAVMode = config->add_subcommand( + "GetTAVMode", "Get TAV mode (NSM Type 5 idx 20)"); + commands.push_back( + std::make_unique<GetTAVMode>("config", "GetTAVMode", getTAVMode)); + + auto setTAVMode = config->add_subcommand( + "SetTAVMode", "Set TAV mode (NSM Type 5 idx 20)"); + commands.push_back( + std::make_unique<SetTAVMode>("config", "SetTAVMode", setTAVMode)); + auto getLTXMode = config->add_subcommand( "GetLTXMode", "Get LTX mode (NSM Type 5 idx DEVICE_MODE_LTX)"); commands.push_back(
diff --git a/nsmtool/test/nsm_config_cmd_branch_test.cpp b/nsmtool/test/nsm_config_cmd_branch_test.cpp index e138967..81bf083 100644 --- a/nsmtool/test/nsm_config_cmd_branch_test.cpp +++ b/nsmtool/test/nsm_config_cmd_branch_test.cpp
@@ -588,7 +588,7 @@ ASSERT_EQ(rc, NSM_SW_SUCCESS); testing::internal::CaptureStdout(); - EXPECT_NO_THROW(commands[24]->parseResponseMsg(msg, buf.size())); + EXPECT_NO_THROW(commands[26]->parseResponseMsg(msg, buf.size())); std::string output = testing::internal::GetCapturedStdout(); EXPECT_NE(output.find("CurrentMode"), std::string::npos); EXPECT_NE(output.find("PendingMode"), std::string::npos); @@ -614,7 +614,7 @@ testing::internal::CaptureStdout(); testing::internal::CaptureStderr(); - EXPECT_NO_THROW(commands[24]->parseResponseMsg(msg, buf.size())); + EXPECT_NO_THROW(commands[26]->parseResponseMsg(msg, buf.size())); std::string stdoutput = testing::internal::GetCapturedStdout(); std::string stderrOutput = testing::internal::GetCapturedStderr(); // No partial-success JSON on stdout ... @@ -629,13 +629,13 @@ setupConfigCommands(app); parseSubcmdArgs(app, "SetLTXMode", {"-M", "Enabled"}); - auto [rc, reqMsg] = commands[25]->createRequestMsg(); + auto [rc, reqMsg] = commands[27]->createRequestMsg(); ASSERT_EQ(rc, NSM_SW_SUCCESS); std::vector<uint8_t> buf(sizeof(nsm_msg_hdr) + sizeof(nsm_common_resp)); auto* msg = reinterpret_cast<nsm_msg*>(buf.data()); encode_set_device_mode_settings_v2_resp(0, NSM_SUCCESS, ERR_NULL, msg); - EXPECT_NO_THROW(commands[25]->parseResponseMsg(msg, buf.size())); + EXPECT_NO_THROW(commands[27]->parseResponseMsg(msg, buf.size())); } TEST(ConfigBranch, SetLTXMode_InvalidMode_ReturnsError) @@ -644,7 +644,7 @@ setupConfigCommands(app); parseSubcmdArgs(app, "SetLTXMode", {"-M", "NotAMode"}); - auto [rc, reqMsg] = commands[25]->createRequestMsg(); + auto [rc, reqMsg] = commands[27]->createRequestMsg(); EXPECT_NE(rc, NSM_SW_SUCCESS); EXPECT_TRUE(reqMsg.empty()); } @@ -672,7 +672,7 @@ ASSERT_EQ(rc, NSM_SW_SUCCESS); testing::internal::CaptureStdout(); - EXPECT_NO_THROW(commands[26]->parseResponseMsg(msg, buf.size())); + EXPECT_NO_THROW(commands[28]->parseResponseMsg(msg, buf.size())); std::string output = testing::internal::GetCapturedStdout(); EXPECT_NE(output.find("CurrentMode"), std::string::npos); EXPECT_NE(output.find("PendingMode"), std::string::npos); @@ -697,7 +697,7 @@ testing::internal::CaptureStdout(); testing::internal::CaptureStderr(); - EXPECT_NO_THROW(commands[26]->parseResponseMsg(msg, buf.size())); + EXPECT_NO_THROW(commands[28]->parseResponseMsg(msg, buf.size())); std::string stdoutput = testing::internal::GetCapturedStdout(); std::string stderrOutput = testing::internal::GetCapturedStderr(); EXPECT_TRUE(stdoutput.empty()); @@ -711,13 +711,13 @@ setupConfigCommands(app); parseSubcmdArgs(app, "SetUPhyMode", {"-M", "Enabled"}); - auto [rc, reqMsg] = commands[27]->createRequestMsg(); + auto [rc, reqMsg] = commands[29]->createRequestMsg(); ASSERT_EQ(rc, NSM_SW_SUCCESS); std::vector<uint8_t> buf(sizeof(nsm_msg_hdr) + sizeof(nsm_common_resp)); auto* msg = reinterpret_cast<nsm_msg*>(buf.data()); encode_set_device_mode_settings_v2_resp(0, NSM_SUCCESS, ERR_NULL, msg); - EXPECT_NO_THROW(commands[27]->parseResponseMsg(msg, buf.size())); + EXPECT_NO_THROW(commands[29]->parseResponseMsg(msg, buf.size())); } TEST(ConfigBranch, SetUPhyMode_InvalidMode_ReturnsError) @@ -726,7 +726,7 @@ setupConfigCommands(app); parseSubcmdArgs(app, "SetUPhyMode", {"-M", "NotAMode"}); - auto [rc, reqMsg] = commands[27]->createRequestMsg(); + auto [rc, reqMsg] = commands[29]->createRequestMsg(); EXPECT_NE(rc, NSM_SW_SUCCESS); EXPECT_TRUE(reqMsg.empty()); }