#include "mps.hpp" namespace phosphor::software::VR { bool MPSImageParser::isValidDataTokens( const std::vector& tokens) { return tokens.size() > static_cast(ATE::regName) && !tokens[0].starts_with('*'); } MPSData MPSImageParser::extractType0Data( const std::vector& tokens) { MPSData data; static constexpr size_t type0TokensSize = 7; if (tokens.size() != type0TokensSize) { lg2::error("Invalid token count for Type0 image line: " "expected {EXPECTED}, got {ACTUAL}", "EXPECTED", type0TokensSize, "ACTUAL", tokens.size()); return data; } data.page = getVal(tokens, ATE::pageNum); data.addr = getVal(tokens, ATE::regAddrHex); std::string regData = getVal(tokens, ATE::regDataHex); size_t byteCount = std::min(regData.length() / 2, size_t(4)); for (size_t i = 0; i < byteCount; ++i) { data.data[byteCount - 1 - i] = static_cast( std::stoul(regData.substr(i * 2, 2), nullptr, 16)); } data.length = static_cast(byteCount); return data; } MPSData MPSImageParser::extractType1Data( const std::vector& tokens) { MPSData data; static constexpr size_t type0TokensSize = 8; if (tokens.size() != type0TokensSize) { lg2::error("Invalid token count for Type1 image line: " "expected {EXPECTED}, got {ACTUAL}", "EXPECTED", type0TokensSize, "ACTUAL", tokens.size()); return data; } data.page = getVal(tokens, ATE::pageNum); auto addr = getVal(tokens, ATE::regAddrHex); auto cmdType = getVal(tokens, ATE::writeType); int blockDataBytes = 0; if (cmdType.starts_with("P")) { // Check if these tokens represent a P1 or P2 process call. // The upper byte of 'addr' is the command code, and the lower byte // is the LSB of the data. // Example: // addr = 0x0F11 and data = 0x18, this sends 0x1811 to command 0x0F static constexpr uint16_t processCallAddrMask = 0xFF00; data.data[0] = static_cast(addr & ~processCallAddrMask); data.data[1] = getVal(tokens, ATE::regDataHex); data.addr = static_cast((addr & processCallAddrMask) >> 8); data.length = 2; return data; } else if (cmdType.starts_with("B")) { // Command types starting with 'B' indicate block r/w commands. // The number following 'B' specifies the number of data bytes. if (cmdType.size() > 1 && std::isdigit(cmdType[1])) { blockDataBytes = std::stoi(cmdType.substr(1)); } } std::string regData = getVal(tokens, ATE::regDataHex); size_t byteCount = std::min(regData.length() / 2, size_t(4)); size_t dataIndex = 0; if (blockDataBytes > 0) { data.data[dataIndex++] = static_cast(blockDataBytes); } for (size_t i = 0; i < byteCount; ++i) { data.data[dataIndex + (byteCount - 1 - i)] = static_cast( std::stoul(regData.substr(i * 2, 2), nullptr, 16)); } data.length = static_cast(dataIndex + byteCount); data.addr = getVal(tokens, ATE::regAddrHex); return data; } std::vector MPSImageParser::parse( const uint8_t* image, size_t imageSize, MPSImageType imageType) { lineTokens = TokenizedLines(image, imageSize); std::vector results; using ExtractDataFunc = std::function&)>; ExtractDataFunc extractDataFunc; switch (imageType) { case MPSImageType::type0: extractDataFunc = [this](const auto& tokens) { return extractType0Data(tokens); }; break; case MPSImageType::type1: extractDataFunc = [this](const auto& tokens) { return extractType1Data(tokens); }; break; default: lg2::error("Unsupported or unknown MPS image type: {TYPE}", "TYPE", static_cast(imageType)); return results; } for (const auto& tokens : lineTokens) { if (tokens[0].starts_with("END")) { break; } if (isValidDataTokens(tokens)) { auto data = extractDataFunc(tokens); if (data.length == 0) { return {}; } results.push_back(data); } } return results; } sdbusplus::async::task MPSVoltageRegulator::parseImage( const uint8_t* image, size_t imageSize, MPSImageType imageType) { configuration = std::make_unique(); try { configuration->registersData = parser->parse(image, imageSize, imageType); if (!co_await parseDeviceConfiguration()) { co_return false; } } catch (const std::exception& e) { lg2::error("Failed to parse MPS image: {ERR}", "ERR", e.what()); co_return false; } lg2::debug( "Parsed configuration: Data Size={SIZE}, Vendor ID={VID}, " "Product ID={PID}, Config ID={CID}, CRC User={CRCUSR}, " "CRC Multi={CRCMULTI}", "SIZE", configuration->registersData.size(), "VID", lg2::hex, configuration->vendorId, "PID", lg2::hex, configuration->productId, "CID", lg2::hex, configuration->configId, "CRCUSR", lg2::hex, configuration->crcUser, "CRCMULTI", lg2::hex, configuration->crcMulti); co_return true; } std::map> MPSVoltageRegulator::getGroupedConfigData(uint8_t configMask, uint8_t shift) { std::map> groupedData; if (!configuration) { return groupedData; } for (const auto& data : configuration->registersData) { uint8_t config = (data.page & configMask) >> shift; groupedData[config].push_back(data); } return groupedData; } } // namespace phosphor::software::VR