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/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