/* * 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 #include #include #include #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 static void set(const std::string& path, const T& value) { std::ofstream file(path); file << value << std::endl; } template static T get(const std::string& path, const T& def) { std::ifstream file(path); T result; file >> result; return file.fail() ? def : result; } Return 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"; return Void(); } } 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 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(LineagePowerHint::SET_PROFILE)) { LOG(VERBOSE) << "PROFILE_POWER_SAVE active, ignoring hint " << static_cast(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(LineagePowerHint::SET_PROFILE)) { setProfile(static_cast(data)); } else if (hint == static_cast(LineagePowerHint::CPU_BOOST)) { sendBoost(data); } else { LOG(INFO) << "Unknown power hint: " << static_cast(hint); } break; } return Void(); } Return Power::setFeature(Feature feature __unused, bool activate __unused) { if (!initialized) { initialize(); } #ifdef TAP_TO_WAKE_NODE if (feature == Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE) { set(TAP_TO_WAKE_NODE, activate ? "1" : "0"); } #endif return Void(); } Return Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) { _hidl_cb({}, Status::SUCCESS); return Void(); } Return Power::getFeature(LineageFeature feature) { switch (feature) { case LineageFeature::SUPPORTED_PROFILES: return static_cast(PowerProfile::MAX); default: return -1; } } void Power::initialize() { findInputNodes(); current_profile = PowerProfile::BALANCED; for (const std::string& interactivePath : cpuInteractivePaths) { hispeed_freqs.emplace_back(get(interactivePath + "/hispeed_freq", "")); } for (const std::string& sysfsPath : cpuSysfsPaths) { max_freqs.emplace_back(get(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(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