| #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 |