samsung: hidl: add exynos hidl power hal

Change-Id: I993353943dd1be7104b6790bfd2d73cd6f63260a
diff --git a/hidl/power/Android.mk b/hidl/power/Android.mk
new file mode 100644
index 0000000..6e5889b
--- /dev/null
+++ b/hidl/power/Android.mk
@@ -0,0 +1,47 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    Power.cpp \
+    service.cpp
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+    hardware/samsung/hidl/light/include
+
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libbinder \
+    libhidlbase \
+    libhidltransport \
+    libutils \
+    android.hardware.power@1.0 \
+    vendor.lineage.power@1.0
+
+LOCAL_STATIC_LIBRARIES := libc++fs
+
+LOCAL_MODULE := android.hardware.power@1.0-service.exynos
+LOCAL_INIT_RC := android.hardware.power@1.0-service.exynos.rc
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_OWNER := samsung
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_EXECUTABLE)
diff --git a/hidl/power/Power.cpp b/hidl/power/Power.cpp
new file mode 100644
index 0000000..721dbda
--- /dev/null
+++ b/hidl/power/Power.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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";
+            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<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();
+    }
+
+#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<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
diff --git a/hidl/power/Power.h b/hidl/power/Power.h
new file mode 100644
index 0000000..2a8b8af
--- /dev/null
+++ b/hidl/power/Power.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_POWER_V1_0_POWER_H
+#define ANDROID_HARDWARE_POWER_V1_0_POWER_H
+
+#include <android/hardware/power/1.0/IPower.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <vendor/lineage/power/1.0/ILineagePower.h>
+
+namespace android {
+namespace hardware {
+namespace power {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::vendor::lineage::power::V1_0::ILineagePower;
+using ::vendor::lineage::power::V1_0::LineageFeature;
+using ::vendor::lineage::power::V1_0::LineagePowerHint;
+
+// clang-format off
+enum PowerProfile {
+    POWER_SAVE = 0,
+    BALANCED,
+    HIGH_PERFORMANCE,
+    MAX
+};
+// clang-format on
+
+struct Power : public IPower, public ILineagePower {
+    Return<void> setInteractive(bool interactive) override;
+    Return<void> powerHint(PowerHint hint, int32_t data) override;
+    Return<void> setFeature(Feature feature, bool activate) override;
+    Return<void> getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) override;
+
+    Return<int32_t> getFeature(LineageFeature feature) override;
+
+  private:
+    void initialize();
+    void findInputNodes();
+    void setProfile(PowerProfile profile);
+    void sendBoostpulse();
+    void sendBoost(int duration_us);
+
+    bool initialized;
+    bool touchkeys_blocked;
+    std::string sec_touchkey;
+    std::string sec_touchscreen;
+    PowerProfile current_profile;
+    std::vector<std::string> hispeed_freqs;
+    std::vector<std::string> max_freqs;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_POWER_V1_0_POWER_H
diff --git a/hidl/power/android.hardware.power@1.0-service.exynos.rc b/hidl/power/android.hardware.power@1.0-service.exynos.rc
new file mode 100644
index 0000000..12dc665
--- /dev/null
+++ b/hidl/power/android.hardware.power@1.0-service.exynos.rc
@@ -0,0 +1,4 @@
+service vendor.power-hal-1-0 /vendor/bin/hw/android.hardware.power@1.0-service.exynos
+    class hal
+    user system
+    group system
diff --git a/hidl/power/include/samsung_power.h b/hidl/power/include/samsung_power.h
new file mode 100644
index 0000000..c85b650
--- /dev/null
+++ b/hidl/power/include/samsung_power.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ * 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.
+ */
+
+#ifndef SAMSUNG_POWER_H
+#define SAMSUNG_POWER_H
+
+/*
+ * Board specific nodes
+ *
+ * If your kernel exposes these controls in another place, you can either
+ * symlink to the locations given here, or override this header in your
+ * device tree.
+ */
+
+static const std::vector<std::string> cpuSysfsPaths = {
+    "/sys/devices/system/cpu/cpu0",
+    "/sys/devices/system/cpu/cpu4"
+};
+
+static const std::vector<std::string> cpuInteractivePaths = {
+    "/sys/devices/system/cpu/cpu0/cpufreq/interactive",
+    "/sys/devices/system/cpu/cpu4/cpufreq/interactive"
+};
+
+/* double tap to wake node */
+//#define TAP_TO_WAKE_NODE "/sys/class/sec/tsp/dt2w_enable"
+
+#endif // SAMSUNG_POWER_H
diff --git a/hidl/power/service.cpp b/hidl/power/service.cpp
new file mode 100644
index 0000000..db3f92d
--- /dev/null
+++ b/hidl/power/service.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+#include <utils/Errors.h>
+
+#include "Power.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::implementation::Power;
+
+using android::OK;
+using android::sp;
+using android::status_t;
+
+int main() {
+    sp<Power> power = new Power();
+    status_t status = 0;
+
+    configureRpcThreadpool(1, true);
+
+    status = power->IPower::registerAsService();
+    if (status != OK) {
+        LOG(ERROR) << "Could not register service (IPower) for Power HAL";
+        goto shutdown;
+    }
+
+    status = power->ILineagePower::registerAsService();
+    if (status != OK) {
+        LOG(ERROR) << "Could not register service (ILineagePower) for Power HAL";
+        goto shutdown;
+    }
+
+    LOG(INFO) << "Power HAL service is Ready.";
+    joinRpcThreadpool();
+
+shutdown:
+    // In normal operation, we don't expect the thread pool to shutdown
+    LOG(ERROR) << "Power HAL failed to join thread pool.";
+    return 1;
+}