| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "power_manager/powerd/system/acpi_wakeup_helper.h" |
| |
| #include <utility> |
| |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/macros.h> |
| #include <base/strings/string_piece.h> |
| #include <base/strings/string_tokenizer.h> |
| |
| namespace power_manager { |
| namespace system { |
| |
| namespace { |
| const base::FilePath kAcpiWakeupPath("/proc/acpi/wakeup"); |
| |
| class AcpiWakeupFile : public AcpiWakeupFileInterface { |
| public: |
| AcpiWakeupFile() {} |
| ~AcpiWakeupFile() override {} |
| |
| bool Exists() override { return base::PathExists(kAcpiWakeupPath); } |
| |
| bool Read(std::string* contents) override { |
| CHECK(contents); |
| return base::ReadFileToString(kAcpiWakeupPath, contents); |
| } |
| |
| bool Write(const std::string& contents) override { |
| int bytes_written = |
| base::WriteFile(kAcpiWakeupPath, contents.data(), contents.size()); |
| return bytes_written == static_cast<int>(contents.size()); |
| } |
| }; |
| } // namespace |
| |
| AcpiWakeupHelper::AcpiWakeupHelper() : file_(new AcpiWakeupFile()) {} |
| |
| AcpiWakeupHelper::~AcpiWakeupHelper() {} |
| |
| void AcpiWakeupHelper::set_file_for_testing( |
| std::unique_ptr<AcpiWakeupFileInterface> file) { |
| file_ = std::move(file); |
| } |
| |
| bool AcpiWakeupHelper::IsSupported() { |
| return file_->Exists(); |
| } |
| |
| bool AcpiWakeupHelper::GetWakeupEnabled(const std::string& device_name, |
| bool* enabled_out) { |
| CHECK(enabled_out); |
| |
| std::string contents; |
| if (!file_->Read(&contents)) { |
| LOG(ERROR) << "Failed to read state from " << kAcpiWakeupPath.value(); |
| return false; |
| } |
| |
| // /proc/acpi/wakeup looks like this: |
| // Device S-state Status Sysfs node |
| // TPAD S3 *enabled pnp:00:00 |
| // |
| // This is a mess to parse, since some of the whitespace is tabs and some is |
| // space padding. To keep things simple, we just look for "enabled/disabled", |
| // which should never conflict with values of other fields anyway. |
| |
| base::StringTokenizer lines(contents, "\n"); |
| while (lines.GetNext()) { |
| std::string line = lines.token(); |
| base::StringTokenizer parts(line, "\t *"); |
| // Check whether first part matches device name. |
| if (!parts.GetNext()) |
| continue; |
| if (parts.token_piece() != device_name) |
| continue; |
| // Find enabled/disabled in later parts. |
| while (parts.GetNext()) { |
| base::StringPiece part = parts.token_piece(); |
| if (part == base::StringPiece("enabled")) { |
| *enabled_out = true; |
| return true; |
| } else if (part == base::StringPiece("disabled")) { |
| *enabled_out = false; |
| return true; |
| } |
| } |
| LOG(WARNING) << "Found device '" << device_name << "' in " |
| << kAcpiWakeupPath.value() |
| << ", but failed to determine wakeup state"; |
| return false; |
| } |
| VLOG(1) << "Device '" << device_name << "' not found in " |
| << kAcpiWakeupPath.value(); |
| return false; |
| } |
| |
| bool AcpiWakeupHelper::SetWakeupEnabled(const std::string& device_name, |
| bool enabled) { |
| // The kernel does not exhibit an interface to set the state directly, we can |
| // only get and toggle. |
| bool readback; |
| if (!GetWakeupEnabled(device_name, &readback)) |
| return false; |
| if (readback != enabled) { |
| if (!ToggleWakeupEnabled(device_name)) |
| return false; |
| if (!GetWakeupEnabled(device_name, &readback) || readback != enabled) |
| return false; |
| VLOG(1) << "ACPI wakeup for " << device_name << " is now " |
| << (enabled ? "enabled" : "disabled"); |
| } |
| return true; |
| } |
| |
| bool AcpiWakeupHelper::ToggleWakeupEnabled(const std::string& device_name) { |
| if (!file_->Write(device_name)) { |
| LOG(ERROR) << "Failed to write to " << kAcpiWakeupPath.value(); |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace system |
| } // namespace power_manager |