blob: eb7ae27f15df4be29f7e9117896d66b010e258e9 [file]
#include "tlbmc/hal/sysfs/led.h"
#include <cstdint>
#include <fstream>
#include <string>
#include <vector>
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "boost/filesystem.hpp" // NOLINT: boost::filesystem is commonly used in BMC
namespace milotic_tlbmc {
namespace {
std::string GetLedAttributePath(absl::string_view sysfs_path,
absl::string_view led_name,
absl::string_view attribute) {
return absl::StrCat(sysfs_path, led_name, "/", attribute);
}
absl::StatusOr<uint32_t> ReadUint32FromFile(const std::string& path) {
std::ifstream file(path);
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to open ", path));
}
std::string content;
std::getline(file, content);
if (!file.good() && !file.eof()) {
return absl::InternalError(absl::StrCat("Failed to read from ", path));
}
uint32_t value;
if (!absl::SimpleAtoi(content, &value)) {
return absl::InternalError(
absl::StrCat("Failed to parse integer from '", content, "' in ", path));
}
return value;
}
absl::Status WriteUint32ToFile(const std::string& path, uint32_t value) {
std::ofstream file(path);
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to open ", path));
}
file << value << "\n";
file.flush();
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to write to ", path));
}
return absl::OkStatus();
}
absl::Status WriteStringToFile(const std::string& path,
absl::string_view value) {
std::ofstream file(path);
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to open ", path));
}
file << value << "\n";
file.flush();
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to write to ", path));
}
return absl::OkStatus();
}
} // namespace
bool LedSysfs::IsLedPresent(absl::string_view led_name) const {
boost::filesystem::path path(absl::StrCat(config_.led_sysfs_path, led_name));
boost::system::error_code ec;
bool exists = boost::filesystem::exists(path, ec);
if (ec) {
LOG(ERROR) << "Error checking exists for path '" << path
<< "': " << ec.message();
return false;
}
return exists;
}
absl::StatusOr<uint32_t> LedSysfs::GetBrightness(
absl::string_view led_name) const {
return ReadUint32FromFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "brightness"));
}
absl::Status LedSysfs::SetBrightness(absl::string_view led_name,
uint32_t brightness) const {
return WriteUint32ToFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "brightness"),
brightness);
}
absl::StatusOr<uint32_t> LedSysfs::GetMaxBrightness(
absl::string_view led_name) const {
return ReadUint32FromFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "max_brightness"));
}
absl::StatusOr<std::string> LedSysfs::GetTrigger(
absl::string_view led_name) const {
std::string path =
GetLedAttributePath(config_.led_sysfs_path, led_name, "trigger");
std::ifstream file(path);
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to open ", path));
}
std::string content;
std::getline(file, content);
if (!file.good() && !file.eof()) {
return absl::InternalError(absl::StrCat("Failed to read from ", path));
}
// The active trigger is enclosed in brackets: "none [timer] heartbeat"
// Find the bracketed trigger.
auto start = content.find('[');
auto end = content.find(']');
if (start == std::string::npos || end == std::string::npos || end <= start) {
return absl::InternalError(absl::StrCat(
"Failed to parse active trigger from '", content, "' in ", path));
}
return content.substr(start + 1, end - start - 1);
}
absl::StatusOr<std::vector<std::string>> LedSysfs::GetAvailableTriggers(
absl::string_view led_name) const {
std::string path =
GetLedAttributePath(config_.led_sysfs_path, led_name, "trigger");
std::ifstream file(path);
if (!file.good()) {
return absl::InternalError(absl::StrCat("Failed to open ", path));
}
std::string content;
std::getline(file, content);
if (!file.good() && !file.eof()) {
return absl::InternalError(absl::StrCat("Failed to read from ", path));
}
// Split by whitespace, stripping brackets from the active trigger.
std::vector<std::string> triggers;
for (absl::string_view token :
absl::StrSplit(content, ' ', absl::SkipEmpty())) {
absl::string_view stripped = token;
absl::ConsumePrefix(&stripped, "[");
absl::ConsumeSuffix(&stripped, "]");
triggers.emplace_back(stripped);
}
return triggers;
}
absl::Status LedSysfs::SetTrigger(absl::string_view led_name,
absl::string_view trigger) const {
return WriteStringToFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "trigger"),
trigger);
}
absl::StatusOr<uint32_t> LedSysfs::GetDelayOn(
absl::string_view led_name) const {
return ReadUint32FromFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "delay_on"));
}
absl::Status LedSysfs::SetDelayOn(absl::string_view led_name,
uint32_t delay_ms) const {
return WriteUint32ToFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "delay_on"),
delay_ms);
}
absl::StatusOr<uint32_t> LedSysfs::GetDelayOff(
absl::string_view led_name) const {
return ReadUint32FromFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "delay_off"));
}
absl::Status LedSysfs::SetDelayOff(absl::string_view led_name,
uint32_t delay_ms) const {
return WriteUint32ToFile(
GetLedAttributePath(config_.led_sysfs_path, led_name, "delay_off"),
delay_ms);
}
} // namespace milotic_tlbmc