blob: 208ba43566720dafb05ac0359a1b8f74755a4158 [file] [log] [blame]
#include "tlbmc/sensors/fan_pwm.h"
#include <array>
#include <charconv>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <ios>
#include <memory>
#include <optional>
#include <string>
#include <system_error> // NOLINT: system_error is commonly used in BMC
#include <utility>
#include "google/protobuf/duration.pb.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/substitute.h"
#include "boost/asio.hpp" // NOLINT: boost::asio is commonly used in BMC
#include "boost/asio/random_access_file.hpp" // NOLINT: boost::asio is commonly used in BMC
#include "boost/filesystem.hpp" // NOLINT: boost::filesystem is commonly used in BMC
#include "boost/filesystem/operations.hpp" // NOLINT: boost::filesystem is commonly used in BMC
#include "boost/system/detail/error_code.hpp" // NOLINT: boost::asio is commonly used in BMC
#include "g3/macros.h"
// NOLINTNEXTLINE: keep this so BUILD file will keep liburing
#include "liburing.h" // IWYU pragma: keep
#include "tlbmc/central_config/config.h"
#include "entity_common_config.pb.h"
#include "fan_pwm_config.pb.h"
#include "i2c_common_config.pb.h"
#include "reading_range_config.pb.h"
#include "threshold_config.pb.h"
#include "tlbmc/hal/sysfs/i2c.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "tlbmc/sensors/fan_controller.h"
#include "tlbmc/sensors/i2c_hwmon_based_sensor.h"
#include "tlbmc/sensors/sensor.h"
#include "tlbmc/time/time.h"
namespace milotic_tlbmc {
absl::Status FanPwm::EnablePwm(const boost::filesystem::path& pwm_enable_path) {
std::fstream file_in(pwm_enable_path.string(), std::ios::in);
if (!file_in.good()) {
return absl::InternalError(
absl::StrCat("Error open ", pwm_enable_path.string()));
}
std::string mode;
std::getline(file_in, mode);
if (mode == "0") {
DLOG(INFO) << "Enabling fan PWM for " << pwm_enable_path.string();
std::ofstream file_out(pwm_enable_path.string(), std::ios::out);
file_out.clear();
file_out.seekp(0, std::ios::beg);
file_out << 1;
file_out.flush();
}
return absl::OkStatus();
}
absl::StatusOr<std::shared_ptr<Sensor>> FanPwm::Create(
const FanPwmConfig& config, const FanController& fan_controller,
const std::shared_ptr<boost::asio::io_context>& io_context,
const I2cSysfs& i2c_sysfs, std::optional<NotificationCb> on_batch_notify) {
DLOG(INFO) << "Creating fan PWM for device: " << absl::StrCat(config);
const I2cCommonConfig& i2c_common_config = config.i2c_common_config();
if (!fan_controller.ControllerHasSensor(i2c_common_config)) {
return absl::InvalidArgumentError(
absl::Substitute("Fan controller $0 does not have sensor $1",
absl::StrCat(fan_controller.GetI2cCommonConfig()),
absl::StrCat(i2c_common_config)));
}
auto it = fan_controller.GetIndexToPwms().find(config.index());
if (it == fan_controller.GetIndexToPwms().end()) {
if (!GetTlbmcConfig().allow_sensor_creation_failure) {
return absl::InvalidArgumentError(absl::Substitute(
"Fan controller $0 does not have a PWM file at index $1",
absl::StrCat(fan_controller.GetI2cCommonConfig()), config.index()));
}
LOG(ERROR) << "Failed to create FanPwm (NONFATAL): "
<< absl::Substitute(
"Fan controller $0 does not have a PWM file at index $1",
absl::StrCat(fan_controller.GetI2cCommonConfig()),
config.index());
std::shared_ptr<FanPwm> fan_pwm = std::make_shared<FanPwm>(
Token(), config.type(), "", absl::StrCat("fanpwm_", config.name()),
config.i2c_common_config(), config.thresholds(),
config.reading_ranges(), config.entity_common_config(), io_context,
on_batch_notify);
State state;
state.set_status(STATUS_CREATION_FAILED);
state.set_status_message("Failed to create FanPwm Sensor (NONFATAL)");
fan_pwm->UpdateState(std::move(state));
return fan_pwm;
}
// Enable PWM
ECCLESIA_RETURN_IF_ERROR(EnablePwm(it->second.pwm_enable));
std::string fan_pwm_key = absl::StrCat("fanpwm_", config.name());
return std::make_shared<FanPwm>(
Token(), config.type(), it->second.pwm.string(), fan_pwm_key,
config.i2c_common_config(), config.thresholds(), config.reading_ranges(),
config.entity_common_config(), io_context, on_batch_notify);
}
void FanPwm::HandleRefreshResult(const boost::system::error_code& error,
size_t bytes_read) {
if (error) {
State state;
state.set_status(STATUS_STALE);
state.set_status_message(absl::Substitute(
"Failed to read from input device: $0; input device path: $1",
error.message(), GetInputDevicePath()));
UpdateState(std::move(state));
return;
}
const char* buffer_end = GetConstReadBuffer().data() + bytes_read;
uint64_t value = 0;
std::from_chars_result result =
std::from_chars(GetConstReadBuffer().data(), buffer_end, value);
if (result.ec != std::errc()) {
State state;
state.set_status(STATUS_STALE);
state.set_status_message(
absl::StrCat("Read data can't be converted to a number: ",
std::make_error_condition(result.ec).message()));
UpdateState(std::move(state));
return;
}
SensorValue sensor_data;
*sensor_data.mutable_timestamp() = Now();
sensor_data.set_reading(((static_cast<double>(value) / 255) * scale_));
StoreSensorData(std::make_shared<const SensorValue>(std::move(sensor_data)));
State state;
state.set_status(STATUS_READY);
UpdateState(std::move(state));
}
FanPwm::FanPwm(Token token, FanPwmType sensor_type,
const std::string& input_dev_path,
const std::string& sensor_name,
const I2cCommonConfig& i2c_common_config,
const ThresholdConfigs& threshold_configs,
const ReadingRangeConfigs& reading_range_configs,
const EntityCommonConfig& entity_common_config,
const std::shared_ptr<boost::asio::io_context>& io_context,
std::optional<NotificationCb> on_batch_notify)
: I2cHwmonBasedSensor(
input_dev_path, io_context,
CreateStaticAttributes(sensor_name, UNIT_PERCENT, i2c_common_config,
entity_common_config, threshold_configs,
reading_range_configs),
on_batch_notify),
sensor_type_(sensor_type),
sensor_name_(sensor_name),
board_config_name_(entity_common_config.board_config_name()) {}
} // namespace milotic_tlbmc