| /* |
| * Copyright (C) 2020 The LineageOS Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "android.hardware.power@1.0-service.exynos" |
| |
| #include "Power.h" |
| #include <android-base/logging.h> |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| #include "samsung_lights.h" |
| #include "samsung_power.h" |
| |
| namespace android { |
| namespace hardware { |
| namespace power { |
| namespace V1_0 { |
| namespace implementation { |
| |
| /* |
| * Write value to path and close file. |
| */ |
| template <typename T> |
| static void set(const std::string& path, const T& value) { |
| std::ofstream file(path); |
| file << value << std::endl; |
| } |
| |
| template <typename T> |
| static T get(const std::string& path, const T& def) { |
| std::ifstream file(path); |
| T result; |
| |
| file >> result; |
| return file.fail() ? def : result; |
| } |
| |
| Return<void> Power::setInteractive(bool interactive) { |
| if (!initialized) { |
| initialize(); |
| } |
| |
| if (!interactive) { |
| int32_t panel_brightness = get(PANEL_BRIGHTNESS_NODE, -1); |
| |
| if (panel_brightness > 0) { |
| LOG(VERBOSE) << "Moving to non-interactive state, but screen is still on," |
| << "not disabling input devices"; |
| goto out; |
| } |
| } |
| |
| if (!sec_touchscreen.empty()) { |
| set(sec_touchscreen, interactive ? "1" : "0"); |
| } |
| |
| if (!sec_touchkey.empty()) { |
| if (!interactive) { |
| int button_state = get(sec_touchkey, -1); |
| |
| if (button_state < 0) { |
| LOG(ERROR) << "Failed to read touchkey state"; |
| goto out; |
| } |
| |
| /* |
| * If button_state is 0, the keys have been disabled by another component |
| * (for example lineagehw), which means we don't want them to be enabled when resuming |
| * from suspend. |
| */ |
| if (button_state == 0) { |
| touchkeys_blocked = true; |
| } |
| } |
| |
| if (!touchkeys_blocked) { |
| set(sec_touchkey, interactive ? "1" : "0"); |
| } |
| } |
| |
| out: |
| for (const std::string& interactivePath : cpuInteractivePaths) { |
| set(interactivePath + "/io_is_busy", interactive ? "1" : "0"); |
| } |
| |
| return Void(); |
| } |
| |
| Return<void> Power::powerHint(PowerHint hint, int32_t data) { |
| if (!initialized) { |
| initialize(); |
| } |
| |
| /* Bail out if low-power mode is active */ |
| if (current_profile == PowerProfile::POWER_SAVE && hint != PowerHint::LOW_POWER && |
| hint != static_cast<PowerHint>(LineagePowerHint::SET_PROFILE)) { |
| LOG(VERBOSE) << "PROFILE_POWER_SAVE active, ignoring hint " << static_cast<int32_t>(hint); |
| return Void(); |
| } |
| |
| switch (hint) { |
| case PowerHint::INTERACTION: |
| case PowerHint::LAUNCH: |
| sendBoostpulse(); |
| break; |
| case PowerHint::LOW_POWER: |
| setProfile(data ? PowerProfile::POWER_SAVE : PowerProfile::BALANCED); |
| break; |
| default: |
| if (hint == static_cast<PowerHint>(LineagePowerHint::SET_PROFILE)) { |
| setProfile(static_cast<PowerProfile>(data)); |
| } else if (hint == static_cast<PowerHint>(LineagePowerHint::CPU_BOOST)) { |
| sendBoost(data); |
| } else { |
| LOG(INFO) << "Unknown power hint: " << static_cast<int32_t>(hint); |
| } |
| break; |
| } |
| return Void(); |
| } |
| |
| Return<void> Power::setFeature(Feature feature __unused, bool activate __unused) { |
| if (!initialized) { |
| initialize(); |
| } |
| |
| if (feature == Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE) { |
| set("/sys/class/sec/tsp/cmd", activate ? "aot_enable,1" : "aot_enable,0"); |
| } |
| |
| return Void(); |
| } |
| |
| Return<void> Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) { |
| _hidl_cb({}, Status::SUCCESS); |
| return Void(); |
| } |
| |
| Return<int32_t> Power::getFeature(LineageFeature feature) { |
| switch (feature) { |
| case LineageFeature::SUPPORTED_PROFILES: |
| return static_cast<int32_t>(PowerProfile::MAX); |
| default: |
| return -1; |
| } |
| } |
| |
| void Power::initialize() { |
| findInputNodes(); |
| |
| current_profile = PowerProfile::BALANCED; |
| |
| for (const std::string& interactivePath : cpuInteractivePaths) { |
| hispeed_freqs.emplace_back(get<std::string>(interactivePath + "/hispeed_freq", "")); |
| } |
| |
| for (const std::string& sysfsPath : cpuSysfsPaths) { |
| max_freqs.emplace_back(get<std::string>(sysfsPath + "/cpufreq/scaling_max_freq", "")); |
| } |
| |
| initialized = true; |
| } |
| |
| void Power::findInputNodes() { |
| std::error_code ec; |
| for (auto& de : std::filesystem::directory_iterator("/sys/class/input/", ec)) { |
| /* we are only interested in the input devices that we can access */ |
| if (ec || de.path().string().find("/sys/class/input/input") == std::string::npos) { |
| continue; |
| } |
| |
| for (auto& de2 : std::filesystem::directory_iterator(de.path(), ec)) { |
| if (!ec && de2.path().string().find("/name") != std::string::npos) { |
| std::string content = get<std::string>(de2.path(), ""); |
| if (content == "sec_touchkey") { |
| sec_touchkey = de.path().string().append("/enabled"); |
| LOG(INFO) << "found sec_touchkey: " << sec_touchkey; |
| } else if (content == "sec_touchscreen") { |
| sec_touchscreen = de.path().string().append("/enabled"); |
| LOG(INFO) << "found sec_touchscreen: " << sec_touchscreen; |
| } |
| } |
| } |
| } |
| } |
| |
| void Power::setProfile(PowerProfile profile) { |
| if (current_profile == profile) { |
| return; |
| } |
| |
| switch (profile) { |
| case PowerProfile::POWER_SAVE: |
| // Limit to hispeed freq |
| for (int i = 0; i < cpuSysfsPaths.size(); i++) { |
| if (hispeed_freqs.size() > i && !hispeed_freqs.at(i).empty()) { |
| set(cpuSysfsPaths.at(i) + "/cpufreq/scaling_max_freq", hispeed_freqs.at(i)); |
| } |
| } |
| break; |
| case PowerProfile::BALANCED: |
| case PowerProfile::HIGH_PERFORMANCE: |
| // Restore normal max freq |
| for (int i = 0; i < cpuSysfsPaths.size(); i++) { |
| if (max_freqs.size() > i && !max_freqs.at(i).empty()) { |
| set(cpuSysfsPaths.at(i) + "/cpufreq/scaling_max_freq", max_freqs.at(i)); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void Power::sendBoostpulse() { |
| // the boostpulse node is only valid for the LITTLE cluster |
| set(cpuInteractivePaths.front() + "/boostpulse", "1"); |
| } |
| |
| void Power::sendBoost(int duration_us) { |
| set(cpuInteractivePaths.front() + "/boost", "1"); |
| |
| usleep(duration_us); |
| |
| set(cpuInteractivePaths.front() + "/boost", "0"); |
| } |
| |
| } // namespace implementation |
| } // namespace V1_0 |
| } // namespace power |
| } // namespace hardware |
| } // namespace android |