From 7cc4ae76eae1f391853b3ce751623165511c1141 Mon Sep 17 00:00:00 2001 From: Demon000 Date: Thu, 27 Sep 2018 11:58:43 +0200 Subject: [PATCH] clover: Import HIDL Lights Hal for xiaomi devices --- device.mk | 4 + light/Android.bp | 33 +++ light/Light.cpp | 242 ++++++++++++++++++ light/Light.h | 59 +++++ ...ardware.light@2.0-service.xiaomi_clover.rc | 25 ++ light/service.cpp | 52 ++++ 6 files changed, 415 insertions(+) create mode 100644 light/Android.bp create mode 100644 light/Light.cpp create mode 100644 light/Light.h create mode 100644 light/android.hardware.light@2.0-service.xiaomi_clover.rc create mode 100644 light/service.cpp diff --git a/device.mk b/device.mk index 8c75c29..3d61e66 100644 --- a/device.mk +++ b/device.mk @@ -40,6 +40,10 @@ PRODUCT_COPY_FILES += \ # Keylayouts PRODUCT_COPY_FILES += \ $(DEVICE_PATH)/keylayout/uinput-fpc.kl:system/usr/keylayout/uinput-fpc.kl + +# Lights +PRODUCT_PACKAGES += \ + android.hardware.light@2.0-service.xiaomi_clover # Device is a Tablet PRODUCT_AAPT_CONFIG := normal large diff --git a/light/Android.bp b/light/Android.bp new file mode 100644 index 0000000..bab1992 --- /dev/null +++ b/light/Android.bp @@ -0,0 +1,33 @@ +//# +//# Copyright 2018 The Android Open Source 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. +//# + +cc_binary { + relative_install_path: "hw", + defaults: ["hidl_defaults"], + name: "android.hardware.light@2.0-service.xiaomi_clover", + proprietary: true, + init_rc: ["android.hardware.light@2.0-service.xiaomi_clover.rc"], + srcs: ["service.cpp", "Light.cpp"], + shared_libs: [ + "libhardware", + "libhidlbase", + "libhidltransport", + "liblog", + "libhwbinder", + "libutils", + "android.hardware.light@2.0", + ], +} diff --git a/light/Light.cpp b/light/Light.cpp new file mode 100644 index 0000000..2b83841 --- /dev/null +++ b/light/Light.cpp @@ -0,0 +1,242 @@ +/* +# +# Copyright 2018 The Android Open Source 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 "LightService" + +#include + +#include "Light.h" + +#include + +#define LEDS "/sys/class/leds/" + +#define LCD_LED LEDS "lcd-backlight/" +#define RED_LED LEDS "red/" + +#define BLINK "blink" +#define BRIGHTNESS "brightness" +#define DUTY_PCTS "duty_pcts" +#define PAUSE_HI "pause_hi" +#define PAUSE_LO "pause_lo" +#define RAMP_STEP_MS "ramp_step_ms" +#define START_IDX "start_idx" + +#define MAX_LED_BRIGHTNESS 255 +#define MAX_LCD_BRIGHTNESS 4095 + +/* + * 8 duty percent steps. + */ +#define RAMP_STEPS 15 +/* + * Each step will stay on for 50ms by default. + */ +#define RAMP_STEP_DURATION 150 +/* + * Each value represents a duty percent (0 - 100) for the led pwm. + */ +static int32_t BRIGHTNESS_RAMP[RAMP_STEPS] = {0, 12, 25, 37, 50, 72, 85, 100, 85, 72, 50, 37, 25, 12, 0}; + +namespace { +/* + * Write value to path and close file. + */ +static void set(std::string path, std::string value) { + std::ofstream file(path); + + if (!file.is_open()) { + ALOGW("failed to write %s to %s", value.c_str(), path.c_str()); + return; + } + + file << value; +} + +static void set(std::string path, int value) { + set(path, std::to_string(value)); +} + +static uint32_t getBrightness(const LightState& state) { + uint32_t alpha, red, green, blue; + + /* + * Extract brightness from AARRGGBB. + */ + alpha = (state.color >> 24) & 0xFF; + red = (state.color >> 16) & 0xFF; + green = (state.color >> 8) & 0xFF; + blue = state.color & 0xFF; + + /* + * Scale RGB brightness if Alpha brightness is not 0xFF. + */ + if (alpha != 0xFF) { + red = red * alpha / 0xFF; + green = green * alpha / 0xFF; + blue = blue * alpha / 0xFF; + } + + return (77 * red + 150 * green + 29 * blue) >> 8; +} + +static inline uint32_t scaleBrightness(uint32_t brightness, uint32_t maxBrightness) { + return brightness * maxBrightness / 0xFF; +} + +static inline uint32_t getScaledBrightness(const LightState& state, uint32_t maxBrightness) { + return scaleBrightness(getBrightness(state), maxBrightness); +} + +static void handleBacklight(Type /* type */, const LightState& state) { + uint32_t brightness = getScaledBrightness(state, MAX_LCD_BRIGHTNESS); + set(LCD_LED BRIGHTNESS, brightness); +} + +/* + * Scale each value of the brightness ramp according to the + * brightness of the color. + */ +static std::string getScaledRamp(uint32_t brightness) { + std::string ramp, pad; + + for (auto const& step : BRIGHTNESS_RAMP) { + ramp += pad + std::to_string(step * brightness / 0xFF); + pad = ","; + } + + return ramp; +} + +static void setNotification(const LightState& state) { + uint32_t redBrightness = getScaledBrightness(state, MAX_LED_BRIGHTNESS); + + /* Disable blinking */ + set(RED_LED BLINK, 0); + + if (state.flashMode == Flash::TIMED) { + /* + * If the flashOnMs duration is not long enough to fit ramping up + * and down at the default step duration, step duration is modified + * to fit. + */ + int32_t stepDuration = RAMP_STEP_DURATION; + int32_t pauseHi = state.flashOnMs - (stepDuration * RAMP_STEPS * 2); + int32_t pauseLo = state.flashOffMs; + + if (pauseHi < 0) { + //stepDuration = state.flashOnMs / (RAMP_STEPS * 2); + pauseHi = 0; + } + + /* Red */ + set(RED_LED START_IDX, 0 * RAMP_STEPS); + set(RED_LED DUTY_PCTS, getScaledRamp(redBrightness)); + set(RED_LED PAUSE_LO, pauseLo); + set(RED_LED PAUSE_HI, pauseHi); + set(RED_LED RAMP_STEP_MS, stepDuration); + + /* Enable blinking */ + set(RED_LED BLINK, 1); + } else { + set(RED_LED BRIGHTNESS, redBrightness); + } +} + +static inline bool isLit(const LightState& state) { + return state.color & 0x00ffffff; +} + +/* + * Keep sorted in the order of importance. + */ +static const LightState offState = {}; +static std::vector> notificationStates = { + { Type::ATTENTION, offState }, + { Type::NOTIFICATIONS, offState }, + { Type::BATTERY, offState }, +}; + +static void handleNotification(Type type, const LightState& state) { + bool handled = false; + + for(auto it : notificationStates) { + if (it.first == type) { + it.second = state; + } + + if (!handled && isLit(it.second)) { + setNotification(it.second); + handled = true; + } + } + + if (!handled) { + setNotification(offState); + } +} + +static std::map> lights = { + { Type::ATTENTION, handleNotification }, + { Type::NOTIFICATIONS, handleNotification }, + { Type::BATTERY, handleNotification }, + { Type::BACKLIGHT, handleBacklight }, +}; + +} // anonymous namespace + +namespace android { +namespace hardware { +namespace light { +namespace V2_0 { +namespace implementation { + +Return Light::setLight(Type type, const LightState& state) { + auto it = lights.find(type); + + if (it == lights.end()) { + return Status::LIGHT_NOT_SUPPORTED; + } + + /* + * Lock global mutex until light state is updated. + */ + std::lock_guard lock(globalLock); + + it->second(type, state); + + return Status::SUCCESS; +} + +Return Light::getSupportedTypes(getSupportedTypes_cb _hidl_cb) { + std::vector types; + + for (auto const& light : lights) { + types.push_back(light.first); + } + + _hidl_cb(types); + + return Void(); +} + +} // namespace implementation +} // namespace V2_0 +} // namespace light +} // namespace hardware +} // namespace android diff --git a/light/Light.h b/light/Light.h new file mode 100644 index 0000000..eb9ff0a --- /dev/null +++ b/light/Light.h @@ -0,0 +1,59 @@ +/* +# +# Copyright 2018 The Android Open Source 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_LIGHT_V2_0_LIGHT_H +#define ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H + +#include +#include +#include +#include +#include +#include + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::light::V2_0::ILight; +using ::android::hardware::light::V2_0::Flash; +using ::android::hardware::light::V2_0::LightState; +using ::android::hardware::light::V2_0::Status; +using ::android::hardware::light::V2_0::Type; + +namespace android { +namespace hardware { +namespace light { +namespace V2_0 { +namespace implementation { + +class Light : public ILight { + public: + Return setLight(Type type, const LightState& state) override; + Return getSupportedTypes(getSupportedTypes_cb _hidl_cb) override; + + private: + std::mutex globalLock; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace light +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H diff --git a/light/android.hardware.light@2.0-service.xiaomi_clover.rc b/light/android.hardware.light@2.0-service.xiaomi_clover.rc new file mode 100644 index 0000000..1b6c099 --- /dev/null +++ b/light/android.hardware.light@2.0-service.xiaomi_clover.rc @@ -0,0 +1,25 @@ +on boot + # White light + chown system system /sys/class/leds/red/brightness + + chown system system /sys/class/leds/red/blink + chown system system /sys/class/leds/red/duty_pcts + chown system system /sys/class/leds/red/pause_hi + chown system system /sys/class/leds/red/pause_lo + chown system system /sys/class/leds/red/ramp_step_ms + chown system system /sys/class/leds/red/start_idx + + chmod 660 /sys/class/leds/red/blink + chmod 660 /sys/class/leds/red/duty_pcts + chmod 660 /sys/class/leds/red/pause_hi + chmod 660 /sys/class/leds/red/pause_lo + chmod 660 /sys/class/leds/red/ramp_step_ms + chmod 660 /sys/class/leds/red/start_idx + + # Button backlight + chown system system /sys/class/leds/button-backlight1/brightness + +service light-hal-2-0 /vendor/bin/hw/android.hardware.light@2.0-service.xiaomi_clover + class hal + user system + group system diff --git a/light/service.cpp b/light/service.cpp new file mode 100644 index 0000000..3d78647 --- /dev/null +++ b/light/service.cpp @@ -0,0 +1,52 @@ +/* +# +# Copyright 2018 The Android Open Source 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.light@2.0-service.xiaomi_sdm660" + +#include + +#include "Light.h" + +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +using android::hardware::light::V2_0::ILight; +using android::hardware::light::V2_0::implementation::Light; + +using android::OK; +using android::sp; +using android::status_t; + +int main() { + android::sp service = new Light(); + + configureRpcThreadpool(1, true); + + status_t status = service->registerAsService(); + if (status != OK) { + ALOGE("Cannot register Light HAL service."); + return 1; + } + + ALOGI("Light HAL service ready."); + + joinRpcThreadpool(); + + ALOGI("Light HAL service failed to join thread pool."); + return 1; +}