This repository was archived by the owner on Oct 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
hardware: Implement our own charging HAL
This commit is a charging control HAL for Sony devices that controls charging by making use of interfaces exposed by Sony's kernel. This HAL provides higher level interfaces for disabling/enabling charging by writing to specific kernel interface. Furthermore, it references pixel's powerstat implementation, uses uevent to monitor power_supply uevents, and provides an interface to allow users to set custom charging limit. Change-Id: Iea037e400326352443b9810a98401cfa15fa6ac5
- Loading branch information
Showing
23 changed files
with
562 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
hidl_package_root { | ||
name: "vendor.sony", | ||
path: "hardware/sony/interfaces/sony", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../../../build/soong/scripts/system-clang-format |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright (C) 2022 The LineageOS Project | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
cc_binary { | ||
name: "vendor.sony.charger-service", | ||
relative_install_path: "hw", | ||
init_rc: ["vendor.sony.charger-service.rc"], | ||
vintf_fragments: ["vendor.sony.charger-service.xml"], | ||
vendor: true, | ||
shared_libs: [ | ||
"libbase", | ||
"libcutils", | ||
"liblog", | ||
"libutils", | ||
"libbinder_ndk", | ||
"vendor.sony.charger-V1-ndk", | ||
], | ||
srcs: [ | ||
"service.cpp", | ||
"Charger.cpp", | ||
"UeventListener.cpp", | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/* | ||
* Copyright (C) 2022 The LineageOS Project | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "Charger.h" | ||
|
||
#include <android-base/logging.h> | ||
#include <android/binder_status.h> | ||
#include <cutils/properties.h> | ||
#include <cutils/uevent.h> | ||
#include <string.h> | ||
#include <fstream> | ||
|
||
namespace aidl { | ||
namespace vendor { | ||
namespace sony { | ||
namespace charger { | ||
|
||
#define LOG_TAG "vendor.sony.charger" | ||
|
||
enum SUPPORTED_BOARD { | ||
kona = 0, | ||
lahaina = 1, | ||
}; | ||
|
||
static constexpr const char* kChgActivationPath[] = { | ||
"/sys/class/power_supply/battery_ext/smart_charging_activation", | ||
"/sys/class/battchg_ext/smart_charging_activation", | ||
}; | ||
|
||
static constexpr const char* kChgInterruptionPath[] = { | ||
"/sys/class/power_supply/battery_ext/smart_charging_interruption", | ||
"/sys/class/battchg_ext/smart_charging_interruption", | ||
}; | ||
|
||
static constexpr const char* kBatCapacityPath[] = { | ||
"/sys/class/power_supply/battery/capacity", | ||
"/sys/class/power_supply/battery/capacity", | ||
}; | ||
|
||
Charger::Charger() { | ||
LOG(INFO) << "Charger HAL Starting..."; | ||
char board_name[PROPERTY_VALUE_MAX]; | ||
|
||
property_get("ro.product.board", board_name, "null"); | ||
|
||
if (strncmp(board_name, "kona", strlen("kona")) == 0) { | ||
chargerBoard = kona; | ||
} else if (strncmp(board_name, "lahaina", strlen("lahaina")) == 0) { | ||
chargerBoard = lahaina; | ||
} else { | ||
LOG(ERROR) << "Failed to get board number, default to kona"; | ||
chargerBoard = kona; | ||
} | ||
chgLimit = 100; | ||
chgStopReason = 0; | ||
} | ||
|
||
bool Charger::updateChargingStatus() { | ||
// If chgStopReason is not 0, we need to stop charging | ||
bool enable = chgStopReason == 0; | ||
|
||
return setChargingEnableInternal(enable); | ||
} | ||
|
||
bool Charger::setChargingEnableInternal(bool enabled) { | ||
LOG(INFO) << (enabled ? "Enable" : "Disable") << " charging"; | ||
std::fstream activationFile(kChgActivationPath[chargerBoard]); | ||
std::fstream interruptionFile(kChgInterruptionPath[chargerBoard]); | ||
int activationResult = -1; | ||
int interruptionResult = -1; | ||
|
||
if (!activationFile.is_open() || !interruptionFile.is_open()) goto error; | ||
|
||
activationFile >> activationResult; | ||
interruptionFile >> interruptionResult; | ||
|
||
if (activationFile.fail() || interruptionFile.fail()) goto error; | ||
|
||
if (activationResult == 0) { | ||
activationFile << "1"; | ||
if (activationFile.fail()) goto error; | ||
} | ||
|
||
if (interruptionResult == enabled) { | ||
interruptionFile << (enabled ? "0" : "1"); | ||
if (interruptionFile.fail()) goto error; | ||
} | ||
|
||
interruptionFile.close(); | ||
activationFile.close(); | ||
|
||
return true; | ||
|
||
error: | ||
LOG(ERROR) << "Failed to read or write charging status: " << strerror(errno); | ||
return false; | ||
} | ||
|
||
ndk::ScopedAStatus Charger::isChargingEnabled(bool* _aidl_return) { | ||
*_aidl_return = chgStopReason & CHG_STOP_REASON_FORCE; | ||
return ndk::ScopedAStatus::ok(); | ||
} | ||
|
||
ndk::ScopedAStatus Charger::setChargingEnable(bool enabled) { | ||
if (enabled) | ||
chgStopReason &= ~CHG_STOP_REASON_FORCE; // clear bit, enable charging | ||
else | ||
chgStopReason |= CHG_STOP_REASON_FORCE; // Set bit, disable charging | ||
|
||
if (updateChargingStatus()) | ||
return ndk::ScopedAStatus::ok(); | ||
else | ||
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC); | ||
} | ||
|
||
ndk::ScopedAStatus Charger::setChargingLimit(int32_t limit) { | ||
LOG(INFO) << "Setting charging limit to " << limit; | ||
if (limit > 100 || limit < 50) { | ||
LOG(ERROR) << "The charging limit " << limit << " is not supported!"; | ||
LOG(ERROR) << " Please select between 50 and 100"; | ||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); | ||
} | ||
|
||
chgLimit = limit; | ||
|
||
return checkBatCapacityAndApplyLimit(); | ||
} | ||
|
||
ndk::ScopedAStatus Charger::checkBatCapacityAndApplyLimit() { | ||
std::ifstream batCapFile(kBatCapacityPath[chargerBoard]); | ||
int batCapNow = -1; | ||
|
||
batCapFile >> batCapNow; | ||
LOG(INFO) << "Capacity: " << batCapNow << ". Limit: " << chgLimit; | ||
|
||
if (chgLimit == 100) { | ||
// We don't limit battery if the limit is 100 | ||
chgStopReason &= ~CHG_STOP_REASON_LIMIT; | ||
} else { | ||
if (!batCapFile.is_open()) goto error; | ||
|
||
if (batCapNow >= chgLimit) { | ||
// Set limit | ||
chgStopReason |= CHG_STOP_REASON_LIMIT; | ||
} else { | ||
chgStopReason &= ~CHG_STOP_REASON_LIMIT; | ||
} | ||
} | ||
|
||
if (updateChargingStatus()) | ||
return ndk::ScopedAStatus::ok(); | ||
else | ||
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC); | ||
|
||
error: | ||
LOG(ERROR) << "Failed to read charging status: " << strerror(errno); | ||
return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC); | ||
} | ||
|
||
} // namespace charger | ||
} // namespace sony | ||
} // namespace vendor | ||
} // namespace aidl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Copyright (C) 2022 The LineageOS Project | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <aidl/vendor/sony/charger/BnCharger.h> | ||
#include <android-base/file.h> | ||
#include "android/binder_status.h" | ||
|
||
namespace aidl { | ||
namespace vendor { | ||
namespace sony { | ||
namespace charger { | ||
|
||
#define CHG_STOP_REASON_FORCE (1 << 0) | ||
#define CHG_STOP_REASON_LIMIT (1 << 1) | ||
|
||
struct Charger : public BnCharger { | ||
Charger(); | ||
ndk::ScopedAStatus setChargingEnable(bool enabled) override; | ||
ndk::ScopedAStatus isChargingEnabled(bool* _aidl_return) override; | ||
ndk::ScopedAStatus setChargingLimit(int32_t limit) override; | ||
ndk::ScopedAStatus checkBatCapacityAndApplyLimit() override; | ||
|
||
private: | ||
bool updateChargingStatus(); | ||
bool setChargingEnableInternal(bool enabled); | ||
int chargerBoard; | ||
int chgLimit; | ||
int chgStopReason; | ||
}; | ||
|
||
} // namespace charger | ||
} // namespace sony | ||
} // namespace vendor | ||
} // namespace aidl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Copyright (C) 2018 The Android Open Source Project | ||
* Copyright (C) 2022 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 "sonycharger-uevent" | ||
|
||
#include "UeventListener.h" | ||
#include <android-base/logging.h> | ||
#include <android/binder_manager.h> | ||
#include <cutils/uevent.h> | ||
|
||
namespace aidl { | ||
namespace vendor { | ||
namespace sony { | ||
namespace charger { | ||
|
||
UeventListener::UeventListener() : uevent_fd_(-1) {} | ||
|
||
std::shared_ptr<ICharger> getChargerService() { | ||
const std::string instance = std::string() + ICharger::descriptor + "/default"; | ||
static bool isChargerDeclared = false; | ||
if (!isChargerDeclared) { | ||
// It is good to cache the result - it would not be changed | ||
isChargerDeclared = AServiceManager_isDeclared(instance.c_str()); | ||
if (!isChargerDeclared) { | ||
LOG(ERROR) << "Charger service is not registered"; | ||
return nullptr; | ||
} | ||
} | ||
|
||
return ICharger::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()))); | ||
} | ||
|
||
void UeventListener::handlePowerSupplyChange() { | ||
LOG(INFO) << "handlePowerSupplyChange HERE"; | ||
const std::shared_ptr<ICharger> charger_client = getChargerService(); | ||
charger_client->checkBatCapacityAndApplyLimit(); | ||
} | ||
|
||
#define UEVENT_MSG_LEN 2048 | ||
bool UeventListener::ProcessUevent() { | ||
char msg[UEVENT_MSG_LEN + 2]; | ||
char* cp; | ||
int n; | ||
|
||
if (uevent_fd_ < 0) { | ||
uevent_fd_ = uevent_open_socket(64 * 1024, true); | ||
if (uevent_fd_ < 0) { | ||
LOG(ERROR) << "uevent_init: uevent_open_socket failed\n"; | ||
return false; | ||
} | ||
} | ||
|
||
n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN); | ||
if (n <= 0 || n >= UEVENT_MSG_LEN) return false; | ||
|
||
// Ensure double-null termination of msg | ||
msg[n] = '\0'; | ||
msg[n + 1] = '\0'; | ||
|
||
/** | ||
* msg is a sequence of null-terminated strings. | ||
* Iterate through and record positions of string/value pairs of interest. | ||
* Double null indicates end of the message. (enforced above) | ||
*/ | ||
cp = msg; | ||
while (*cp) { | ||
if (!strncmp(cp, "SUBSYSTEM=power_supply", strlen("SUBSYSTEM=power_supply"))) { | ||
handlePowerSupplyChange(); | ||
break; | ||
} | ||
|
||
while (*cp++) | ||
; | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
void UeventListener::ListenForever() { | ||
constexpr int kMaxConsecutiveErrors = 10; | ||
int consecutive_errors = 0; | ||
|
||
while (1) { | ||
if (ProcessUevent()) { | ||
consecutive_errors = 0; | ||
} else { | ||
if (++consecutive_errors == kMaxConsecutiveErrors) { | ||
LOG(ERROR) << "Too many ProcessUevent errors; exiting UeventListener"; | ||
return; | ||
} | ||
} | ||
} | ||
} | ||
|
||
} // namespace charger | ||
} // namespace sony | ||
} // namespace vendor | ||
} // namespace aidl |
Oops, something went wrong.