| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_NIC_VEEPROM_ACCESSOR_BASE_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_NIC_VEEPROM_ACCESSOR_BASE_H_ |
| |
| #include <endian.h> |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <cstring> |
| #include <fstream> |
| #include <ios> |
| #include <numeric> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "google/protobuf/timestamp.pb.h" |
| #include "absl/base/thread_annotations.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_format.h" |
| #include "absl/synchronization/mutex.h" |
| #include "absl/time/clock.h" |
| #include "absl/time/time.h" |
| #include "tlbmc/hal/nic_veeprom/interface.h" |
| #include "veeprom.pb.h" |
| #include "sensor.pb.h" |
| |
| namespace milotic_tlbmc::nic_veeprom { |
| namespace internal { |
| |
| // Maximum size of the VEEPROM file. |
| constexpr size_t kMaxVeepromSize = 4096; |
| // Magic number for VEEPROM header. |
| constexpr uint32_t kHeaderMagic = 0x424d4353; |
| |
| // Validates checksum for a buffer, assuming checksum field is 0. |
| inline bool ValidateChecksum(const std::vector<uint8_t>& data) { |
| uint8_t sum = std::accumulate(data.begin(), data.end(), uint8_t{0}); |
| return sum == 0; |
| } |
| |
| // Reads VEEPROM data of type T from the given sysfs path. |
| template <typename T> |
| absl::StatusOr<std::vector<uint8_t>> ReadVeeprom(const std::string& path) { |
| std::ifstream ifs(path, std::ios::binary | std::ios::ate); |
| if (!ifs) { |
| return absl::UnavailableError( |
| absl::StrFormat("Failed to open VEEPROM file: %s", path)); |
| } |
| std::streamsize size = ifs.tellg(); |
| if (size < sizeof(T)) { |
| return absl::UnavailableError(absl::StrFormat( |
| "VEEPROM file size %d is smaller than expected %d", size, sizeof(T))); |
| } |
| ifs.seekg(kMaxVeepromSize - sizeof(T), std::ios::beg); |
| std::vector<uint8_t> buf(sizeof(T)); |
| if (!ifs.read(reinterpret_cast<char*>(buf.data()), sizeof(T))) { |
| return absl::UnavailableError( |
| absl::StrFormat("Failed to read VEEPROM file: %s", path)); |
| } |
| return buf; |
| } |
| |
| template <typename T> |
| class AccessorBase : public Accessor { |
| public: |
| explicit AccessorBase(const std::string& eeprom_path, int version) |
| : eeprom_path_(eeprom_path), version_(version) {} |
| |
| // Reads VEEPROM data, validates checksum, version and header magic, |
| // and updates the internal sensor cache. |
| void DoRefresh() override { |
| absl::MutexLock lock(mutex_); |
| sensor_values_ = std::nullopt; |
| auto buf = ReadVeeprom<T>(eeprom_path_); |
| if (!buf.ok()) { |
| LOG(ERROR) << buf.status(); |
| return; |
| } |
| |
| if (!ValidateChecksum(*buf)) { |
| LOG(ERROR) << "VEEPROM V" << version_ << " checksum validation failed"; |
| return; |
| } |
| uint16_t version; |
| memcpy(&version, buf->data() + offsetof(T, version), sizeof(version)); |
| version = be16toh(version); |
| if (version != version_) { |
| LOG(ERROR) << "VEEPROM V" << version_ << " version mismatch: " << version; |
| return; |
| } |
| |
| uint32_t header_magic; |
| memcpy(&header_magic, buf->data() + offsetof(T, sensor_header_magic), |
| sizeof(header_magic)); |
| header_magic = be32toh(header_magic); |
| if (header_magic != kHeaderMagic) { |
| LOG(ERROR) << "VEEPROM V" << version_ |
| << " header magic mismatch: " << header_magic; |
| return; |
| } |
| |
| T data; |
| memcpy(&data, buf->data(), sizeof(data)); |
| sensor_values_ = data; |
| last_refresh_timestamp_ = absl::Now(); |
| } |
| |
| protected: |
| const std::string eeprom_path_; |
| const int version_; |
| mutable absl::Mutex mutex_; |
| std::optional<T> sensor_values_ ABSL_GUARDED_BY(mutex_); |
| absl::Time last_refresh_timestamp_ ABSL_GUARDED_BY(mutex_); |
| }; |
| |
| } // namespace internal |
| } // namespace milotic_tlbmc::nic_veeprom |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_NIC_VEEPROM_ACCESSOR_BASE_H_ |