#pragma once #include "common/include/i2c/i2c.hpp" #include "i2c-vr/vr.hpp" #include #include #include #include #include #include namespace phosphor::software::VR { /** * @brief * Columns of an Automated Test Equipment (ATE) format configuration file. * Each enumerator corresponds to a tab-separated column index. */ enum class ATE : uint8_t { configId = 0, pageNum, regAddrHex, regAddrDec, regName, regDataHex, regDataDec, writeType, colCount, }; enum class MPSPage : uint8_t { page0 = 0, page1, page2, page3, page4, page29 = 0x29, page2A = 0x2A, }; struct MPSData { uint8_t page = 0; uint8_t addr = 0; uint8_t length = 0; std::array data{}; }; struct MPSConfig { uint32_t vendorId = 0; uint32_t productId = 0; uint32_t configId = 0; uint32_t crcUser = 0; uint32_t crcMulti = 0; std::vector registersData; }; /** * @brief * Utility class to iterate over lines and tokenize them by tab characters. */ class TokenizedLines { public: TokenizedLines(const uint8_t* d, size_t s) : data(reinterpret_cast(d), s) {} /** * @brief Iterator over tokenized lines. */ struct Iterator { using iterator_category = std::forward_iterator_tag; using value_type = std::vector; using difference_type = std::ptrdiff_t; using pointer = const value_type*; using reference = const value_type&; Iterator() = default; // End iterator Iterator(std::string_view sv) : remaining(sv) { next(); } reference operator*() const { return currentTokens; } pointer operator->() const { return ¤tTokens; } Iterator& operator++() { next(); return *this; } Iterator operator++(int) { auto result = *this; ++(*this); return result; } friend bool operator==(const Iterator& a, const Iterator& b) { return a.remaining.empty() && b.remaining.empty(); } friend bool operator!=(const Iterator& a, const Iterator& b) { return !(a == b); } private: std::string_view remaining; std::vector currentTokens; void next() { currentTokens.clear(); if (remaining.empty()) { return; } // Extract current line auto newlinePos = remaining.find('\n'); std::string_view line = remaining.substr(0, newlinePos); remaining = (newlinePos == std::string_view::npos) ? std::string_view{} : remaining.substr(newlinePos + 1); // Tokenize by tab size_t start = 0; while (start < line.size()) { start = line.find_first_not_of('\t', start); if (start == std::string_view::npos) { break; } auto end = line.find('\t', start); currentTokens.emplace_back(line.substr(start, end - start)); start = (end == std::string_view::npos) ? line.size() : end; } } }; Iterator begin() const { return Iterator(data); } static Iterator end() { return Iterator(); } private: std::string_view data; }; /** * @brief Base parser for MPS configuration images. */ class MPSImageParser { public: MPSImageParser(const uint8_t* image, size_t imageSize) : lineTokens(image, imageSize) {} template inline static constexpr bool always_false = false; /** * @brief Extract a typed value from a tokenized line. * @tparam T Return type (string or integral type) * @param tokens Tokenized line * @param index Column index (ATE enum) * @return Parsed value or default if invalid */ template T getVal(const std::vector& tokens, ATE index) { size_t idx = static_cast(index); if (tokens.size() <= idx) { lg2::error("Index out of range for ATE enum: {INDEX}", "INDEX", static_cast(idx)); return T{}; } std::string_view token = tokens[idx]; if constexpr (std::is_same_v) { return std::string(token); } else if constexpr (std::is_integral_v) { unsigned long val = 0; try { val = std::stoul(std::string(token), nullptr, 16); } catch (...) { lg2::error("Invalid hex value: {INDEX}", "INDEX", static_cast(idx)); return T{}; } return static_cast(val); } else { static_assert(always_false, "Unsupported type in getVal"); } } /** * @brief Check if a tokenized line contains valid register data. */ static bool isValidDataTokens(const std::vector& tokens); /** * @brief Convert tokenized line into MPSData structure. */ MPSData extractData(const std::vector& tokens); /** * @brief Collect all register data entries from the parsed image. */ std::vector getRegistersData(); TokenizedLines lineTokens; }; /** * @brief Base class for MPS Voltage Regulators. */ class MPSVoltageRegulator : public VoltageRegulator { public: MPSVoltageRegulator(sdbusplus::async::context& ctx, uint16_t bus, uint16_t address) : VoltageRegulator(ctx), i2cInterface(phosphor::i2c::I2C(bus, address)) {} /** * @brief Parse device-specific configuration from the loaded image. * @return async task returning true if parsing succeeds */ virtual sdbusplus::async::task parseDeviceConfiguration() = 0; /** * @brief Parse an image file into internal MPS configuration. * @param image Pointer to the image data * @param imageSize Size of the image data * @return async task returning true if parsing succeeds */ sdbusplus::async::task parseImage(const uint8_t* image, size_t imageSize); /** * @brief Group register data by page, optionally masked and shifted. * @param configMask Bitmask to select relevant page bits (default 0xFF) * @param shift Number of bits to shift masked value to obtain group key * @return map of page keys to vector of MPSData */ std::map> getGroupedConfigData( uint8_t configMask = 0xFF, uint8_t shift = 0); protected: phosphor::i2c::I2C i2cInterface; std::unique_ptr parser; std::unique_ptr configuration; }; } // namespace phosphor::software::VR