blob: 83b3a304bc9a713cac2dfd97b117e7583cfd3b43 [file]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_FRU_SCANNER_I2C_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_FRU_SCANNER_I2C_H_
#include <sys/types.h>
#include <charconv>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "bus_topology.pb.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/statusor.h"
#include "absl/strings/ascii.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "io/ioctl.h"
#include "io/smbus/kernel_dev.h"
#include "io/smbus/smbus.h"
#include "time/clock.h"
#include "tlbmc/deterministic_bmc/i2c_walker/i2c_walker_interface.h"
#include "tlbmc/deterministic_bmc/i2c_walker/uhmm_i2c_walker.h"
#include "tlbmc/hal/fru_scanner.h"
#include "tlbmc/hal/sysfs/i2c.h"
#include "tlbmc/utils/fru_utils.h"
namespace milotic_tlbmc {
constexpr int kI2cFruScanStart = 0x50;
constexpr int kI2cFruScanEnd = 0x57;
#ifdef TLBMC_FRU_SCAN_EXTRA_ADDRS
inline constexpr uint32_t kI2cFruScanExtraAddrsStorage[] = {
TLBMC_FRU_SCAN_EXTRA_ADDRS};
inline constexpr absl::Span<const uint32_t> kI2cFruScanExtraAddrs =
kI2cFruScanExtraAddrsStorage;
#else
inline constexpr absl::Span<const uint32_t> kI2cFruScanExtraAddrs = {};
#endif
// Context for the scan.
struct ScanContext {
// Map of bus to addresses that have already been scanned.
absl::flat_hash_map<size_t, absl::flat_hash_set<size_t>> fru_addresses_list;
};
class FruScannerI2c : public FruScanner {
public:
struct Options {
std::string root_dir;
std::string i2c_dev_dir;
absl::flat_hash_map<size_t, std::optional<absl::flat_hash_set<size_t>>>
bus_block_list;
// For deterministic BMC
bool deterministic_scan = false;
uhmm::I2cRoot i2c_root;
std::string i2c_devices_sysfs_dir;
std::vector<uint32_t> extra_fru_scan_addresses = std::vector<uint32_t>(
kI2cFruScanExtraAddrs.begin(), kI2cFruScanExtraAddrs.end());
};
static std::unique_ptr<FruScannerI2c> Create(const Options& options);
absl::StatusOr<std::vector<std::unique_ptr<I2cFruInfo>>> ScanAllI2cFrus()
const override;
absl::StatusOr<std::vector<std::unique_ptr<I2cSensorAssemblyFruInfo>>>
ScanAllI2cSensorAssemblyFru() const override;
absl::StatusOr<std::unique_ptr<I2cFruInfo>> GetI2cFruInfoFromBus(
int bus, int address, absl::Duration delay_between_reads) const override;
absl::StatusOr<std::unique_ptr<I2cFruInfo>> ScanDirectI2cFru(
int bus, int address, absl::Duration delay_between_reads) const;
bool IsDeterministicScanEnabled() const { return deterministic_scan_; }
protected:
FruScannerI2c(
absl::string_view root_dir, absl::string_view i2c_dev_dir,
bool deterministic_scan, uhmm::I2cRoot i2c_root,
absl::string_view i2c_devices_sysfs_dir,
const absl::flat_hash_map<
size_t, std::optional<absl::flat_hash_set<size_t>>>& bus_block_list,
std::vector<uint32_t> extra_fru_scan_addresses = std::vector<uint32_t>(
kI2cFruScanExtraAddrs.begin(), kI2cFruScanExtraAddrs.end()))
: root_dir_(root_dir),
deterministic_scan_(deterministic_scan),
uhmm_i2c_walker_(deterministic_bmc::UhmmI2cWalker::Create({
.root_directory = root_dir_,
.i2c_root = i2c_root,
})),
i2c_root_(std::move(i2c_root)),
i2c_sysfs_(milotic_tlbmc::I2cSysfsConfig{
.i2c_sysfs_path = std::string(i2c_devices_sysfs_dir)}),
bus_block_list_(bus_block_list),
sys_ioctl_(),
smbus_access_(std::make_unique<ecclesia::KernelSmbusAccess>(
std::string(i2c_dev_dir), &sys_ioctl_)),
extra_fru_scan_addresses_(std::move(extra_fru_scan_addresses)),
extra_fru_scan_addresses_set_(extra_fru_scan_addresses_.begin(),
extra_fru_scan_addresses_.end()) {}
enum class AddressingMode {
k8Bit,
k16Bit,
kUnknown,
};
void FindI2CDevices(const absl::flat_hash_set<int>& i2c_buses,
BusMap& bus_map) const;
std::pair<std::vector<uint8_t>, bool> ReadFruContentsFromI2c(
int i2c_bus, int address,
absl::Duration delay_between_reads = absl::ZeroDuration(),
bool* parsing_error = nullptr) const;
// Reads 16-bit addressed FRU data using raw I2C block read
// (ReadBlock16BitAddr). This is atomic at the bus level.
int64_t Read16BitFru(const ecclesia::SmbusLocation& location, off_t offset,
absl::Span<uint8_t> outbuf,
absl::Duration delay_between_reads) const;
// Reads FRU data using SMBus block read protocol (ReadBlockI2C).
// This is atomic for reads up to 32 bytes (SMBus limit). For larger reads,
// it chunks the read in user space, which is not fully atomic across chunks.
// Prefer this by default for efficiency and atomicity.
int64_t Read8BitFru(const ecclesia::SmbusLocation& location, off_t offset,
absl::Span<uint8_t> outbuf,
absl::Duration delay_between_reads) const;
AddressingMode GetDeviceAddressingMode(
const ecclesia::SmbusLocation& location) const;
// Returns the list of valid FRU addresses found on the given i2c bus.
// devices: Map of address to FRU data.
// skip_list: List of addresses to skip.
// root_found: List of addresses found at root bus.
absl::flat_hash_set<size_t> FindI2cEeproms(
int i2cBus, const std::shared_ptr<DeviceMap>& devices,
absl::string_view root_dir,
const absl::flat_hash_set<size_t>& root_blocked_addresses,
absl::flat_hash_set<size_t>& root_found_addresses) const;
std::string root_dir_;
bool deterministic_scan_ = false;
std::unique_ptr<deterministic_bmc::I2cWalkerInterface> uhmm_i2c_walker_;
uhmm::I2cRoot i2c_root_;
I2cSysfs i2c_sysfs_;
const absl::flat_hash_map<size_t, std::optional<absl::flat_hash_set<size_t>>>
bus_block_list_;
ecclesia::SysIoctl sys_ioctl_;
std::unique_ptr<ecclesia::SmbusAccessInterface> smbus_access_;
ecclesia::Clock* clock_ = ecclesia::Clock::RealClock();
std::vector<uint32_t> extra_fru_scan_addresses_;
absl::flat_hash_set<size_t> extra_fru_scan_addresses_set_;
};
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_HAL_FRU_SCANNER_I2C_H_