power: Add 1.0 HAL for devices that don't have WiFi stats
Some devices (cough Broadcom cough) don't have nodes that return WiFi
stats, so add a 1.0 HAL that devices can build. Also actually enable
the system power stats code and use it. Also add a legacy stats option
for devices that use an old RPM driver
Change-Id: If946b81b589ce11b0bde46437396bb4be74a731a
diff --git a/Android.mk b/Android.mk
index 7c457d1..cfc28fe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -30,8 +30,7 @@
libhidlbase \
libhidltransport \
libhardware \
- libutils \
- android.hardware.power@1.1
+ libutils
LOCAL_SRC_FILES := \
service.cpp \
@@ -115,8 +114,24 @@
LOCAL_CFLAGS += -DTAP_TO_WAKE_NODE=\"$(TARGET_TAP_TO_WAKE_NODE)\"
endif
+ifeq ($(TARGET_HAS_LEGACY_POWER_STATS),true)
+ LOCAL_CFLAGS += -DLEGACY_STATS
+endif
+
+ifneq ($(TARGET_WLAN_POWER_STAT),)
+ LOCAL_CFLAGS += -DWLAN_POWER_STAT=\"$(TARGET_WLAN_POWER_STAT)\"
+endif
+
+ifeq ($(TARGET_HAS_NO_WIFI_STATS),true)
+LOCAL_MODULE := android.hardware.power@1.0-service-qti
+LOCAL_INIT_RC := android.hardware.power@1.0-service-qti.rc
+LOCAL_SHARED_LIBRARIES += android.hardware.power@1.0
+LOCAL_CFLAGS += -DV1_0_HAL
+else
LOCAL_MODULE := android.hardware.power@1.1-service-qti
LOCAL_INIT_RC := android.hardware.power@1.1-service-qti.rc
+LOCAL_SHARED_LIBRARIES += android.hardware.power@1.1
+endif
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_OWNER := qcom
LOCAL_VENDOR_MODULE := true
diff --git a/Power.cpp b/Power.cpp
index 5b838ff..bb4bb50 100644
--- a/Power.cpp
+++ b/Power.cpp
@@ -15,7 +15,11 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.power@1.1-service.wahoo"
+#ifdef V1_0_HAL
+#define LOG_TAG "android.hardware.power@1.0-service-qti"
+#else
+#define LOG_TAG "android.hardware.power@1.1-service-qti"
+#endif
#include <android/log.h>
#include <utils/Log.h>
@@ -31,14 +35,20 @@
namespace android {
namespace hardware {
namespace power {
+#ifdef V1_0_HAL
+namespace V1_0 {
+#else
namespace V1_1 {
+#endif
namespace implementation {
using ::android::hardware::power::V1_0::Feature;
using ::android::hardware::power::V1_0::PowerHint;
using ::android::hardware::power::V1_0::PowerStatePlatformSleepState;
using ::android::hardware::power::V1_0::Status;
+#ifndef V1_0_HAL
using ::android::hardware::power::V1_1::PowerStateSubsystem;
+#endif
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -64,21 +74,66 @@
}
Return<void> Power::getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) {
-#if 0 // we don't do anything with this
hidl_vec<PowerStatePlatformSleepState> states;
uint64_t stats[MAX_PLATFORM_STATS * MAX_RPM_PARAMS] = {0};
uint64_t *values;
struct PowerStatePlatformSleepState *state;
int ret;
- states.resize(PLATFORM_SLEEP_MODES_COUNT);
-
ret = extract_platform_stats(stats);
if (ret != 0) {
states.resize(0);
goto done;
}
+#ifdef LEGACY_STATS
+ states.resize(RPM_MODE_MAX);
+
+ /* Update statistics for XO_shutdown */
+ state = &states[RPM_MODE_XO];
+ state->name = "XO_shutdown";
+
+ state->residencyInMsecSinceBoot = stats[ACCUMULATED_VLOW_TIME];
+ state->totalTransitions = stats[VLOW_COUNT];
+ state->supportedOnlyInSuspend = false;
+ state->voters.resize(XO_VOTERS);
+
+ /* Update statistics for APSS voter */
+ state->voters[0].name = "APSS";
+ state->voters[0].totalTimeInMsecVotedForSinceBoot =
+ stats[XO_ACCUMULATED_DURATION_APSS] / RPM_CLK;
+ state->voters[0].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_APSS];
+
+ /* Update statistics for MPSS voter */
+ state->voters[1].name = "MPSS";
+ state->voters[1].totalTimeInMsecVotedForSinceBoot =
+ stats[XO_ACCUMULATED_DURATION_MPSS] / RPM_CLK;
+ state->voters[1].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_MPSS];
+
+ /* Update statistics for ADSP voter */
+ state->voters[2].name = "ADSP";
+ state->voters[2].totalTimeInMsecVotedForSinceBoot =
+ stats[XO_ACCUMULATED_DURATION_ADSP] / RPM_CLK;
+ state->voters[2].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_ADSP];
+
+ /* Update statistics for SLPI voter */
+ state->voters[3].name = "SLPI";
+ state->voters[3].totalTimeInMsecVotedForSinceBoot =
+ stats[XO_ACCUMULATED_DURATION_SLPI] / RPM_CLK;
+ state->voters[3].totalNumberOfTimesVotedSinceBoot = stats[XO_COUNT_SLPI];
+
+ /* Update statistics for VMIN state */
+ state = &states[RPM_MODE_VMIN];
+
+ state->name = "VMIN";
+ state->residencyInMsecSinceBoot = stats[ACCUMULATED_VMIN_TIME];
+ state->totalTransitions = stats[VMIN_COUNT];
+ state->supportedOnlyInSuspend = false;
+ state->voters.resize(VMIN_VOTERS);
+ //Note: No filling of state voters since VMIN_VOTERS = 0
+#else
+ states.resize(PLATFORM_SLEEP_MODES_COUNT);
+
/* Update statistics for XO_shutdown */
state = &states[RPM_MODE_XO];
state->name = "XO_shutdown";
@@ -106,13 +161,15 @@
state->supportedOnlyInSuspend = false;
state->voters.resize(VMIN_VOTERS);
//Note: No filling of state voters since VMIN_VOTERS = 0
-
+#endif
done:
_hidl_cb(states, Status::SUCCESS);
-#endif
return Void();
}
+#ifndef V1_0_HAL
+// Methods from ::android::hardware::power::V1_1::IPower follow.
+
static int get_wlan_low_power_stats(struct PowerStateSubsystem &subsystem) {
uint64_t stats[WLAN_POWER_PARAMS_COUNT] = {0};
@@ -145,16 +202,15 @@
return 0;
}
-// Methods from ::android::hardware::power::V1_1::IPower follow.
Return<void> Power::getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) {
hidl_vec<PowerStateSubsystem> subsystems;
int ret;
- subsystems.resize(SUBSYSTEM_COUNT);
+ subsystems.resize(subsystem_type::SUBSYSTEM_COUNT);
//We currently have only one Subsystem for WLAN
- ret = get_wlan_low_power_stats(subsystems[SUBSYSTEM_WLAN]);
+ ret = get_wlan_low_power_stats(subsystems[subsystem_type::SUBSYSTEM_WLAN]);
if (ret != 0)
goto done;
@@ -169,9 +225,10 @@
// just call the normal power hint in this oneway function
return powerHint(hint, data);
}
+#endif
} // namespace implementation
-} // namespace V1_1
+} // namespace V1_0/1
} // namespace power
} // namespace hardware
} // namespace android
diff --git a/Power.h b/Power.h
index 130d2f2..1c85a6d 100644
--- a/Power.h
+++ b/Power.h
@@ -18,7 +18,11 @@
#ifndef ANDROID_HARDWARE_POWER_V1_1_POWER_H
#define ANDROID_HARDWARE_POWER_V1_1_POWER_H
+#ifdef V1_0_HAL
+#include <android/hardware/power/1.0/IPower.h>
+#else
#include <android/hardware/power/1.1/IPower.h>
+#endif
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <hardware/power.h>
@@ -26,12 +30,20 @@
namespace android {
namespace hardware {
namespace power {
+#ifdef V1_0_HAL
+namespace V1_0 {
+#else
namespace V1_1 {
+#endif
namespace implementation {
using ::android::hardware::power::V1_0::Feature;
using ::android::hardware::power::V1_0::PowerHint;
+#ifdef V1_0_HAL
+using ::android::hardware::power::V1_0::IPower;
+#else
using ::android::hardware::power::V1_1::IPower;
+#endif
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -45,16 +57,18 @@
Return<void> setFeature(Feature feature, bool activate) override;
Return<void> getPlatformLowPowerStats(getPlatformLowPowerStats_cb _hidl_cb) override;
+#ifndef V1_0_HAL
// Methods from ::android::hardware::power::V1_1::IPower follow.
Return<void> getSubsystemLowPowerStats(getSubsystemLowPowerStats_cb _hidl_cb) override;
Return<void> powerHintAsync(PowerHint hint, int32_t data) override;
+#endif
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
} // namespace implementation
-} // namespace V1_1
+} // namespace V1_0/1
} // namespace power
} // namespace hardware
} // namespace android
diff --git a/android.hardware.power@1.0-service-qti.rc b/android.hardware.power@1.0-service-qti.rc
new file mode 100644
index 0000000..01739a0
--- /dev/null
+++ b/android.hardware.power@1.0-service-qti.rc
@@ -0,0 +1,4 @@
+service power-hal-1-0 /vendor/bin/hw/android.hardware.power@1.0-service-qti
+ class hal
+ user system
+ group system
diff --git a/power-helper.c b/power-helper.c
index 1e19c7d..db864a9 100644
--- a/power-helper.c
+++ b/power-helper.c
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2017 The LineageOS Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -35,6 +37,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
+#include <inttypes.h>
#include <stdlib.h>
#include <unistd.h>
@@ -50,13 +53,79 @@
#include "power-feature.h"
#include "power-helper.h"
+#define USINSEC 1000000L
+#define NSINUS 1000L
+
+#ifndef RPM_STAT
+#define RPM_STAT "/d/rpm_stats"
+#endif
+
+#ifndef RPM_MASTER_STAT
+#define RPM_MASTER_STAT "/d/rpm_master_stats"
+#endif
+
+#ifndef RPM_SYSTEM_STAT
+#define RPM_SYSTEM_STAT "/d/system_stats"
+#endif
+
+/*
+ Set with TARGET_WLAN_POWER_STAT in BoardConfig.mk
+ Defaults to QCACLD3 path
+ Path for QCACLD3: /d/wlan0/power_stats
+ Path for QCACLD2 and Prima: /d/wlan_wcnss/power_stats
+ */
+
+#ifndef V1_0_HAL
#ifndef WLAN_POWER_STAT
#define WLAN_POWER_STAT "/d/wlan0/power_stats"
#endif
+#endif
#define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0]))
#define LINE_SIZE 128
+#ifdef LEGACY_STATS
+/* Use these stats on pre-nougat qualcomm kernels */
+static const char *rpm_param_names[] = {
+ "vlow_count",
+ "accumulated_vlow_time",
+ "vmin_count",
+ "accumulated_vmin_time"
+};
+
+static const char *rpm_master_param_names[] = {
+ "xo_accumulated_duration",
+ "xo_count",
+ "xo_accumulated_duration",
+ "xo_count",
+ "xo_accumulated_duration",
+ "xo_count",
+ "xo_accumulated_duration",
+ "xo_count"
+};
+#else
+/* Use these stats on nougat and forward */
+const char *rpm_stat_params[MAX_RPM_PARAMS] = {
+ "count",
+ "actual last sleep(msec)",
+};
+
+const char *master_stat_params[MAX_RPM_PARAMS] = {
+ "Accumulated XO duration",
+ "XO Count",
+};
+
+struct stat_pair rpm_stat_map[] = {
+ { RPM_MODE_XO, "RPM Mode:vlow", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
+ { RPM_MODE_VMIN, "RPM Mode:vmin", rpm_stat_params, ARRAY_SIZE(rpm_stat_params) },
+ { VOTER_APSS, "APSS", master_stat_params, ARRAY_SIZE(master_stat_params) },
+ { VOTER_MPSS, "MPSS", master_stat_params, ARRAY_SIZE(master_stat_params) },
+ { VOTER_ADSP, "ADSP", master_stat_params, ARRAY_SIZE(master_stat_params) },
+ { VOTER_SLPI, "SLPI", master_stat_params, ARRAY_SIZE(master_stat_params) },
+};
+#endif
+
+#ifndef V1_0_HAL
const char *wlan_power_stat_params[] = {
"cumulative_sleep_time_ms",
"cumulative_total_on_time_ms",
@@ -67,6 +136,7 @@
struct stat_pair wlan_stat_map[] = {
{ WLAN_POWER_DEBUG_STATS, "POWER DEBUG STATS", wlan_power_stat_params, ARRAY_SIZE(wlan_power_stat_params) },
};
+#endif
static int saved_dcvs_cpu0_slack_max = -1;
static int saved_dcvs_cpu0_slack_min = -1;
@@ -405,7 +475,7 @@
(strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
undo_hint_action(DISPLAY_STATE_HINT_ID);
display_hint_sent = 0;
- } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
+ } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
(strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
if (saved_interactive_mode == -1 || saved_interactive_mode == 0) {
/* Display turned on. Restore if possible. */
@@ -514,6 +584,84 @@
return 0;
}
+#ifdef LEGACY_STATS
+static int extract_stats(uint64_t *list, char *file, const char**param_names,
+ unsigned int num_parameters, int isHex) {
+ FILE *fp;
+ ssize_t read;
+ size_t len;
+ size_t index = 0;
+ char *line;
+ int ret;
+
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ ret = -errno;
+ ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
+ return ret;
+ }
+
+ for (line = NULL, len = 0;
+ ((read = getline(&line, &len, fp) != -1) && (index < num_parameters));
+ free(line), line = NULL, len = 0) {
+ uint64_t value;
+ char* offset;
+
+ size_t begin = strspn(line, " \t");
+ if (strncmp(line + begin, param_names[index], strlen(param_names[index]))) {
+ continue;
+ }
+
+ offset = memchr(line, ':', len);
+ if (!offset) {
+ continue;
+ }
+
+ if (isHex) {
+ sscanf(offset, ":%" SCNx64, &value);
+ } else {
+ sscanf(offset, ":%" SCNu64, &value);
+ }
+ list[index] = value;
+ index++;
+ }
+
+ free(line);
+ fclose(fp);
+
+ return 0;
+}
+
+int extract_platform_stats(uint64_t *list) {
+ int ret;
+ //Data is located in two files
+ ret = extract_stats(list, RPM_STAT, rpm_param_names, RPM_PARAM_COUNT, false);
+ if (ret) {
+ for (size_t i=0; i < RPM_PARAM_COUNT; i++)
+ list[i] = 0;
+ }
+ ret = extract_stats(list + RPM_PARAM_COUNT, RPM_MASTER_STAT,
+ rpm_master_param_names, PLATFORM_PARAM_COUNT - RPM_PARAM_COUNT, true);
+ if (ret) {
+ for (size_t i=RPM_PARAM_COUNT; i < PLATFORM_PARAM_COUNT; i++)
+ list[i] = 0;
+ }
+ return 0;
+}
+
+#ifndef V1_0_HAL
+int extract_wlan_stats(uint64_t *list) {
+ int ret;
+ ret = extract_stats(list, WLAN_POWER_STAT, wlan_param_names, WLAN_PARAM_COUNT, false);
+ if (ret) {
+ for (size_t i=0; i < WLAN_PARAM_COUNT; i++)
+ list[i] = 0;
+ }
+ return 0;
+}
+#endif
+#else
+
static int extract_stats(uint64_t *list, char *file,
struct stat_pair *map, size_t map_size) {
FILE *fp;
@@ -522,27 +670,33 @@
char *line;
size_t i, stats_read = 0;
int ret = 0;
+
fp = fopen(file, "re");
if (fp == NULL) {
ALOGE("%s: failed to open: %s Error = %s", __func__, file, strerror(errno));
return -errno;
}
+
line = malloc(len);
if (!line) {
ALOGE("%s: no memory to hold line", __func__);
fclose(fp);
return -ENOMEM;
}
+
while ((stats_read < map_size) && (read = getline(&line, &len, fp) != -1)) {
size_t begin = strspn(line, " \t");
+
for (i = 0; i < map_size; i++) {
if (!strncmp(line + begin, map[i].label, strlen(map[i].label))) {
stats_read++;
break;
}
}
+
if (i == map_size)
continue;
+
ret = parse_stats(map[i].parameters, map[i].num_parameters,
&list[map[i].stat * MAX_RPM_PARAMS], fp);
if (ret < 0)
@@ -550,9 +704,17 @@
}
free(line);
fclose(fp);
+
return ret;
}
+int extract_platform_stats(uint64_t *list) {
+ return extract_stats(list, RPM_SYSTEM_STAT, rpm_stat_map, ARRAY_SIZE(rpm_stat_map));
+}
+
+#ifndef V1_0_HAL
int extract_wlan_stats(uint64_t *list) {
return extract_stats(list, WLAN_POWER_STAT, wlan_stat_map, ARRAY_SIZE(wlan_stat_map));
}
+#endif
+#endif
diff --git a/power-helper.h b/power-helper.h
index d7862c7..3d22353 100644
--- a/power-helper.h
+++ b/power-helper.h
@@ -36,20 +36,57 @@
#include "hardware/power.h"
+#ifdef LEGACY_STATS
+enum platform_param_id {
+ VLOW_COUNT = 0,
+ ACCUMULATED_VLOW_TIME,
+ VMIN_COUNT,
+ ACCUMULATED_VMIN_TIME,
+ RPM_PARAM_COUNT,
+
+ XO_ACCUMULATED_DURATION_APSS = RPM_PARAM_COUNT,
+ XO_COUNT_APSS,
+ XO_ACCUMULATED_DURATION_MPSS,
+ XO_COUNT_MPSS,
+ XO_ACCUMULATED_DURATION_ADSP,
+ XO_COUNT_ADSP,
+ XO_ACCUMULATED_DURATION_SLPI,
+ XO_COUNT_SLPI,
+
+ //Don't add any lines after that line
+ PLATFORM_PARAM_COUNT
+};
+#endif
enum stats_type {
+ //Platform Stats
+ RPM_MODE_XO = 0,
+ RPM_MODE_VMIN,
+ RPM_MODE_MAX,
+ XO_VOTERS_START = RPM_MODE_MAX,
+ VOTER_APSS = XO_VOTERS_START,
+ VOTER_MPSS,
+ VOTER_ADSP,
+ VOTER_SLPI,
+ MAX_PLATFORM_STATS,
+
+#ifndef V1_0_HAL
//WLAN Stats
WLAN_POWER_DEBUG_STATS = 0,
MAX_WLAN_STATS,
+#endif
};
enum subsystem_type {
+#ifndef V1_0_HAL
SUBSYSTEM_WLAN = 0,
+#endif
//Don't add any lines after this line
SUBSYSTEM_COUNT
};
+#ifndef V1_0_HAL
enum wlan_sleep_states {
WLAN_STATE_ACTIVE = 0,
WLAN_STATE_DEEP_SLEEP,
@@ -67,10 +104,16 @@
//Don't add any lines after this line
WLAN_POWER_PARAMS_COUNT
};
+#endif
+#define PLATFORM_SLEEP_MODES_COUNT RPM_MODE_MAX
#define MAX_RPM_PARAMS 2
+#ifdef LEGACY_STATS
+#define XO_VOTERS 4
+#else
#define XO_VOTERS (MAX_PLATFORM_STATS - XO_VOTERS_START)
+#endif
#define VMIN_VOTERS 0
struct stat_pair {
@@ -85,8 +128,10 @@
void power_hint(power_hint_t hint, void *data);
void power_set_interactive(int on);
void set_feature(feature_t feature, int state);
+int extract_platform_stats(uint64_t *list);
+#ifndef V1_0_HAL
int extract_wlan_stats(uint64_t *list);
-
+#endif
#ifdef __cplusplus
}
diff --git a/service.cpp b/service.cpp
index d119052..ffb1f2a 100644
--- a/service.cpp
+++ b/service.cpp
@@ -15,7 +15,11 @@
* limitations under the License.
*/
+#ifdef V1_0_HAL
+#define LOG_TAG "android.hardware.power@1.0-service-qti"
+#else
#define LOG_TAG "android.hardware.power@1.1-service-qti"
+#endif
#include <android/log.h>
#include <hidl/HidlTransportSupport.h>
@@ -31,15 +35,24 @@
using android::hardware::joinRpcThreadpool;
// Generated HIDL files
+#ifdef V1_0_HAL
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::implementation::Power;
+#else
using android::hardware::power::V1_1::IPower;
using android::hardware::power::V1_1::implementation::Power;
+#endif
int main() {
status_t status;
android::sp<IPower> service = nullptr;
+#ifdef V1_0_HAL
+ ALOGI("Power HAL Service 1.0 for QCOM is starting.");
+#else
ALOGI("Power HAL Service 1.1 for QCOM is starting.");
+#endif
service = new Power();
if (service == nullptr) {