From f036db8030328e6b17f7790cdba2880b16d57ff5 Mon Sep 17 00:00:00 2001 From: Demon000 Date: Thu, 14 May 2020 11:44:48 +0200 Subject: [PATCH] sdm660-common: Rework Light HAL based on xiaomi-msm8998 source * squash from here https://github.com/xiaomi-msm8998/device_xiaomi_msm8998-common/tree/afafb3b3f398cede950eeb369e1f205b2a4d9cf8/light * Added automatic max brightness detection * Rework debug Statments The xiaomi-sdm660-devs one created Led Light issues Change-Id: Ie7b62a488770734e659f4db03436ebaa2f2f5f30 --- light/Android.bp | 6 +- light/Light.cpp | 372 ++++++++++++++++++++++++---------------------- light/Light.h | 63 ++++---- light/service.cpp | 7 +- 4 files changed, 233 insertions(+), 215 deletions(-) diff --git a/light/Android.bp b/light/Android.bp index 7a2474b0..40e062c4 100644 --- a/light/Android.bp +++ b/light/Android.bp @@ -1,5 +1,4 @@ -// -// Copyright (C) 2018 The Android Open Source Project +// Copyright (C) 2018 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. @@ -20,8 +19,11 @@ cc_binary { srcs: ["service.cpp", "Light.cpp"], shared_libs: [ "libbase", + "libhardware", "libhidlbase", "libhidltransport", + "liblog", + "libhwbinder", "libutils", "android.hardware.light@2.0", ], diff --git a/light/Light.cpp b/light/Light.cpp index 3915682d..2eac8c87 100644 --- a/light/Light.cpp +++ b/light/Light.cpp @@ -1,6 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project - * Copyright (C) 2020 The LineageOS Project + * Copyright (C) 2018 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. @@ -17,63 +16,96 @@ #define LOG_TAG "android.hardware.light@2.0-service.xiaomi_sdm660" +#include + #include "Light.h" -#include -#include -#include +#include -#include +#define LEDS "/sys/class/leds/" + +#define BUTTON1_LED LEDS "button-backlight1/" +#define BUTTON_LED LEDS "button-backlight/" +#define LCD_LED LEDS "lcd-backlight/" +#define WHITE_LED LEDS "white/" +#define RED_LED LEDS "red/" + +#define BLINK "blink" +#define BRIGHTNESS "brightness" +#define MAX_BRIGHTNESS "max_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_WHITE_LED_BRIGHTNESS 255 +#define MAX_RED_LED_BRIGHTNESS 255 +#define MAX_LCD_BRIGHTNESS 4095 + +/* + * 8 duty percent steps. + */ +#define RAMP_STEPS 8 +/* + * 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}; namespace { +/* + * Write value to path and close file. + */ +static void set(std::string path, std::string value) { + std::ofstream file(path); -#define LEDS "/sys/class/leds/" -#define LCD_LED LEDS "lcd-backlight/" -#define WHITE LEDS "white/" -#define RED LEDS "red/" -#define BUTTON LEDS "button-backlight/" -#define BUTTON1 LEDS "button-backlight1/" -#define BRIGHTNESS "brightness" -#define MAX_BRIGHTNESS "max_brightness" -#define BLINK "blink" -#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" + if (!file.is_open()) { + LOG(DEBUG) << "failed to write %s to %s" << value.c_str() << path.c_str(); + return; + } -using ::android::base::ReadFileToString; -using ::android::base::WriteStringToFile; - -// Default max brightness -constexpr auto kDefaultMaxLedBrightness = 255; -constexpr auto kDefaultMaxScreenBrightness = 4095; - -// Each step will stay on for 150ms by default. -constexpr auto kRampStepDuration = 150; - -// Each value represents a duty percent (0 - 100) for the led pwm. -constexpr std::array kBrightnessRamp = {0, 12, 25, 37, 50, 72, 85, 100}; - -// Write value to path and close file. -bool WriteToFile(const std::string& path, uint32_t content) { - return WriteStringToFile(std::to_string(content), path); + file << value; } -bool WriteToFile(const std::string& path, const std::string& content) { - return WriteStringToFile(content, path); +/* + * Read value to path and close file. + */ +static int get(std::string path) { + std::ifstream file(path); + int value; + + if (!file.is_open()) { + LOG(DEBUG) << "failed to read from %s" << path.c_str(); + return 0; + } + + file >> value; + return value; } -uint32_t RgbaToBrightness(uint32_t color) { - // Extract brightness from AARRGGBB. - uint32_t alpha = (color >> 24) & 0xFF; +static void set(std::string path, int value) { + set(path, std::to_string(value)); +} - // Retrieve each of the RGB colors - uint32_t red = (color >> 16) & 0xFF; - uint32_t green = (color >> 8) & 0xFF; - uint32_t blue = color & 0xFF; +static uint32_t getBrightness(const LightState& state) { + uint32_t alpha, red, green, blue; - // Scale RGB colors if a brightness has been applied by the user + /* + * 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; @@ -83,29 +115,105 @@ uint32_t RgbaToBrightness(uint32_t color) { return (77 * red + 150 * green + 29 * blue) >> 8; } -inline uint32_t RgbaToBrightness(uint32_t color, uint32_t max_brightness) { - return RgbaToBrightness(color) * max_brightness / 0xFF; +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 int getMaxBrightness(std::string path) { + int value = get(path); + LOG(DEBUG) << "Got max brightness %d" << value; + return value; +} + +static void handleBacklight(const LightState& state) { + uint32_t brightness = getScaledBrightness(state, getMaxBrightness(LCD_LED MAX_BRIGHTNESS)); + set(LCD_LED BRIGHTNESS, brightness); +} + +static void handleButtons(const LightState& state) { + uint32_t brightness = getScaledBrightness(state, getMaxBrightness(BUTTON_LED MAX_BRIGHTNESS)); + set(BUTTON_LED BRIGHTNESS, brightness); + set(BUTTON1_LED BRIGHTNESS, brightness); } /* * Scale each value of the brightness ramp according to the * brightness of the color. */ -std::string GetScaledDutyPcts(uint32_t brightness) { - std::stringstream ramp; +static std::string getScaledRamp(uint32_t brightness) { + std::string ramp, pad; - for (size_t i = 0; i < kBrightnessRamp.size(); i++) { - if (i > 0) ramp << ","; - ramp << kBrightnessRamp[i] * brightness / 0xFF; + for (auto const& step : BRIGHTNESS_RAMP) { + ramp += pad + std::to_string(step * brightness / 0xFF); + pad = ","; } - return ramp.str(); + return ramp; } -inline bool IsLit(uint32_t color) { - return color & 0x00ffffff; +static void handleNotification(const LightState& state) { + uint32_t whiteBrightness = getScaledBrightness(state, getMaxBrightness(WHITE_LED MAX_BRIGHTNESS)); + uint32_t redBrightness = getScaledBrightness(state, getMaxBrightness(RED_LED MAX_BRIGHTNESS)); + + /* Disable blinking */ + set(WHITE_LED BLINK, 0); + 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; + } + + /* White */ + set(WHITE_LED START_IDX, 0 * RAMP_STEPS); + set(WHITE_LED DUTY_PCTS, getScaledRamp(whiteBrightness)); + set(WHITE_LED PAUSE_LO, pauseLo); + set(WHITE_LED PAUSE_HI, pauseHi); + set(WHITE_LED RAMP_STEP_MS, stepDuration); + + /* 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(WHITE_LED BLINK, 1); + set(RED_LED BLINK, 1); + } else { + set(WHITE_LED BRIGHTNESS, whiteBrightness); + set(RED_LED BRIGHTNESS, redBrightness); + } } +static inline bool isLit(const LightState& state) { + return state.color & 0x00ffffff; +} + +/* Keep sorted in the order of importance. */ +static std::vector backends = { + { Type::ATTENTION, handleNotification }, + { Type::NOTIFICATIONS, handleNotification }, + { Type::BATTERY, handleNotification }, + { Type::BACKLIGHT, handleBacklight }, + { Type::BUTTONS, handleButtons }, +}; + } // anonymous namespace namespace android { @@ -114,58 +222,39 @@ namespace light { namespace V2_0 { namespace implementation { -Light::Light() { - std::string buf; - - if (ReadFileToString(LCD_LED MAX_BRIGHTNESS, &buf)) { - max_screen_brightness_ = std::stoi(buf); - } else { - max_screen_brightness_ = kDefaultMaxScreenBrightness; - LOG(ERROR) << "Failed to read max screen brightness, fallback to " - << kDefaultMaxLedBrightness; - } - - if (ReadFileToString(WHITE MAX_BRIGHTNESS, &buf)) { - max_led_brightness_ = std::stoi(buf); - } else { - max_led_brightness_ = kDefaultMaxLedBrightness; - LOG(ERROR) << "Failed to read max white LED brightness, fallback to " << kDefaultMaxLedBrightness; - } - - if (ReadFileToString(RED MAX_BRIGHTNESS, &buf)) { - max_red_led_brightness_= std::stoi(buf); - } else { - max_red_led_brightness_ = kDefaultMaxLedBrightness; - LOG(ERROR) << "Failed to read max red LED brightness, fallback to " << kDefaultMaxLedBrightness; - } - - if (!access(BUTTON BRIGHTNESS, W_OK)) { - lights_.emplace(std::make_pair(Type::BUTTONS, - [this](auto&&... args) { setLightButtons(args...); })); - buttons_.emplace_back(BUTTON BRIGHTNESS); - - if (!access(BUTTON1 BRIGHTNESS, W_OK)) { - buttons_.emplace_back(BUTTON1 BRIGHTNESS); - } - - if (ReadFileToString(BUTTON MAX_BRIGHTNESS, &buf)) { - max_button_brightness_ = std::stoi(buf); - } else { - max_button_brightness_ = kDefaultMaxLedBrightness; - LOG(ERROR) << "Failed to read max button brightness, fallback to " - << kDefaultMaxLedBrightness; - } - } -} - Return Light::setLight(Type type, const LightState& state) { - auto it = lights_.find(type); + LightStateHandler handler; + bool handled = false; - if (it == lights_.end()) { + /* Lock global mutex until light state is updated. */ + std::lock_guard lock(globalLock); + + /* Update the cached state value for the current type. */ + for (LightBackend& backend : backends) { + if (backend.type == type) { + backend.state = state; + handler = backend.handler; + } + } + + /* If no handler has been found, then the type is not supported. */ + if (!handler) { return Status::LIGHT_NOT_SUPPORTED; } - it->second(type, state); + /* Light up the type with the highest priority that matches the current handler. */ + for (LightBackend& backend : backends) { + if (handler == backend.handler && isLit(backend.state)) { + handler(backend.state); + handled = true; + break; + } + } + + /* If no type has been lit up, then turn off the hardware. */ + if (!handled) { + handler(state); + } return Status::SUCCESS; } @@ -173,86 +262,15 @@ Return Light::setLight(Type type, const LightState& state) { Return Light::getSupportedTypes(getSupportedTypes_cb _hidl_cb) { std::vector types; - for (auto&& light : lights_) types.emplace_back(light.first); + for (const LightBackend& backend : backends) { + types.push_back(backend.type); + } _hidl_cb(types); return Void(); } -void Light::setLightBacklight(Type /*type*/, const LightState& state) { - uint32_t brightness = RgbaToBrightness(state.color, max_screen_brightness_); - WriteToFile(LCD_LED BRIGHTNESS, brightness); -} - -void Light::setLightButtons(Type /*type*/, const LightState& state) { - uint32_t brightness = RgbaToBrightness(state.color, max_button_brightness_); - for (auto&& button : buttons_) { - WriteToFile(button, brightness); - } -} - -void Light::setLightNotification(Type type, const LightState& state) { - bool found = false; - for (auto&& [cur_type, cur_state] : notif_states_) { - if (cur_type == type) { - cur_state = state; - } - - // Fallback to battery light - if (!found && (cur_type == Type::BATTERY || IsLit(state.color))) { - found = true; - LOG(DEBUG) << __func__ << ": type=" << toString(cur_type); - applyNotificationState(state); - } - } -} - -void Light::applyNotificationState(const LightState& state) { - uint32_t white_brightness = RgbaToBrightness(state.color, max_led_brightness_); - uint32_t red_brightness = RgbaToBrightness(state.color, max_red_led_brightness_); - - // Turn off the leds (initially) - WriteToFile(WHITE BLINK, 0); - WriteToFile(RED BLINK, 0); - - if (state.flashMode == Flash::TIMED && state.flashOnMs > 0 && state.flashOffMs > 0) { - /* - * 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 step_duration = kRampStepDuration; - int32_t pause_hi = state.flashOnMs - (step_duration * kBrightnessRamp.size() * 2); - if (pause_hi < 0) { - step_duration = state.flashOnMs / (kBrightnessRamp.size() * 2); - pause_hi = 0; - } - - LOG(DEBUG) << __func__ << ": color=" << std::hex << state.color << std::dec - << " onMs=" << state.flashOnMs << " offMs=" << state.flashOffMs; - - // White - WriteToFile(WHITE START_IDX, 0); - WriteToFile(WHITE DUTY_PCTS, GetScaledDutyPcts(white_brightness)); - WriteToFile(WHITE PAUSE_LO, static_cast(state.flashOffMs)); - WriteToFile(WHITE PAUSE_HI, static_cast(pause_hi)); - WriteToFile(WHITE RAMP_STEP_MS, static_cast(step_duration)); - WriteToFile(WHITE BLINK, 1); - - // Red - WriteToFile(RED START_IDX, 0); - WriteToFile(RED DUTY_PCTS, GetScaledDutyPcts(red_brightness)); - WriteToFile(RED PAUSE_LO, static_cast(state.flashOffMs)); - WriteToFile(RED PAUSE_HI, static_cast(pause_hi)); - WriteToFile(RED RAMP_STEP_MS, static_cast(step_duration)); - WriteToFile(RED BLINK, 1); - } else { - WriteToFile(WHITE BRIGHTNESS, white_brightness); - WriteToFile(RED BRIGHTNESS, red_brightness); - } -} - } // namespace implementation } // namespace V2_0 } // namespace light diff --git a/light/Light.h b/light/Light.h index 99eade48..4f102d8d 100644 --- a/light/Light.h +++ b/light/Light.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2020 The LineageOS Project + * Copyright (C) 2018 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. @@ -14,11 +14,35 @@ * limitations under the License. */ -#pragma once +#ifndef ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H +#define ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H #include +#include +#include +#include +#include +#include -#include +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::light::V2_0::Flash; +using ::android::hardware::light::V2_0::ILight; +using ::android::hardware::light::V2_0::LightState; +using ::android::hardware::light::V2_0::Status; +using ::android::hardware::light::V2_0::Type; + +typedef void (*LightStateHandler)(const LightState&); + +struct LightBackend { + Type type; + LightState state; + LightStateHandler handler; + + LightBackend(Type type, LightStateHandler handler) : type(type), handler(handler) { + this->state.color = 0xff000000; + } +}; namespace android { namespace hardware { @@ -26,42 +50,13 @@ namespace light { namespace V2_0 { namespace implementation { -using ::android::hardware::Return; -using ::android::hardware::light::V2_0::ILight; -using ::android::hardware::light::V2_0::LightState; -using ::android::hardware::light::V2_0::Status; -using ::android::hardware::light::V2_0::Type; - class Light : public ILight { public: - Light(); - Return setLight(Type type, const LightState& state) override; Return getSupportedTypes(getSupportedTypes_cb _hidl_cb) override; private: - void setLightBacklight(Type type, const LightState& state); - void setLightButtons(Type type, const LightState& state); - void setLightNotification(Type type, const LightState& state); - void applyNotificationState(const LightState& state); - - uint32_t max_button_brightness_; - uint32_t max_led_brightness_; - uint32_t max_red_led_brightness_; - uint32_t max_screen_brightness_; - - std::unordered_map> lights_{ - {Type::BACKLIGHT, [this](auto&&... args) { setLightBacklight(args...); }}, - {Type::BATTERY, [this](auto&&... args) { setLightNotification(args...); }}, - {Type::NOTIFICATIONS, [this](auto&&... args) { setLightNotification(args...); }}}; - - // Keep sorted in the order of importance. - std::array, 2> notif_states_ = {{ - {Type::NOTIFICATIONS, {}}, - {Type::BATTERY, {}}, - }}; - - std::vector buttons_; + std::mutex globalLock; }; } // namespace implementation @@ -69,3 +64,5 @@ class Light : public ILight { } // namespace light } // namespace hardware } // namespace android + +#endif // ANDROID_HARDWARE_LIGHT_V2_0_LIGHT_H diff --git a/light/service.cpp b/light/service.cpp index 27a4ebf3..19c02e35 100644 --- a/light/service.cpp +++ b/light/service.cpp @@ -1,6 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project - * Copyright (C) 2020 The LineageOS Project + * Copyright (C) 2018 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. @@ -25,13 +24,15 @@ 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(); + sp service = new Light(); configureRpcThreadpool(1, true);