1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <ctype.h>
19#include <dirent.h>
20#include <inttypes.h>
21#include <stdlib.h>
22#include <string.h>
23
24#define LOG_TAG "ThermalHAL"
25#include <utils/Log.h>
26
27#include <hardware/hardware.h>
28#include <hardware/thermal.h>
29
30#define CPU_LABEL               "CPU"
31#define MAX_LENGTH              50
32
33#define CPU_USAGE_FILE          "/proc/stat"
34#define TEMPERATURE_DIR         "/sys/class/thermal"
35#define THERMAL_DIR             "thermal_zone"
36#define CPU_ONLINE_FILE_FORMAT  "/sys/devices/system/cpu/cpu%d/online"
37#define UNKNOWN_LABEL           "UNKNOWN"
38
39static ssize_t get_temperatures(thermal_module_t *module, temperature_t *list, size_t size) {
40    char file_name[MAX_LENGTH];
41    FILE *file;
42    float temp;
43    size_t idx = 0;
44    DIR *dir;
45    struct dirent *de;
46
47    /** Read all available temperatures from
48     * /sys/class/thermal/thermal_zone[0-9]+/temp files.
49     * Don't guarantee that all temperatures are in Celsius. */
50    dir = opendir(TEMPERATURE_DIR);
51    if (dir == 0) {
52        ALOGE("%s: failed to open directory %s: %s", __func__, TEMPERATURE_DIR, strerror(-errno));
53        return -errno;
54    }
55
56    while ((de = readdir(dir))) {
57        if (!strncmp(de->d_name, THERMAL_DIR, strlen(THERMAL_DIR))) {
58            snprintf(file_name, MAX_LENGTH, "%s/%s/temp", TEMPERATURE_DIR, de->d_name);
59            file = fopen(file_name, "r");
60            if (file == NULL) {
61                continue;
62            }
63            if (1 != fscanf(file, "%f", &temp)) {
64                fclose(file);
65                continue;
66            }
67
68            if (list != NULL && idx < size) {
69                list[idx] = (temperature_t) {
70                    .name = UNKNOWN_LABEL,
71                    .type = DEVICE_TEMPERATURE_UNKNOWN,
72                    .current_value = temp,
73                    .throttling_threshold = UNKNOWN_TEMPERATURE,
74                    .shutdown_threshold = UNKNOWN_TEMPERATURE,
75                    .vr_throttling_threshold = UNKNOWN_TEMPERATURE,
76                };
77            }
78            fclose(file);
79            idx++;
80        }
81    }
82    closedir(dir);
83    return idx;
84}
85
86static ssize_t get_cpu_usages(thermal_module_t *module, cpu_usage_t *list) {
87    int vals, cpu_num, online;
88    ssize_t read;
89    uint64_t user, nice, system, idle, active, total;
90    char *line = NULL;
91    size_t len = 0;
92    size_t size = 0;
93    char file_name[MAX_LENGTH];
94    FILE *cpu_file;
95    FILE *file = fopen(CPU_USAGE_FILE, "r");
96
97    if (file == NULL) {
98        ALOGE("%s: failed to open: %s", __func__, strerror(errno));
99        return -errno;
100    }
101
102    while ((read = getline(&line, &len, file)) != -1) {
103        // Skip non "cpu[0-9]" lines.
104        if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) {
105            free(line);
106            line = NULL;
107            len = 0;
108            continue;
109        }
110        vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user,
111                &nice, &system, &idle);
112
113        free(line);
114        line = NULL;
115        len = 0;
116
117        if (vals != 5) {
118            ALOGE("%s: failed to read CPU information from file: %s", __func__, strerror(errno));
119            fclose(file);
120            return errno ? -errno : -EIO;
121        }
122
123        active = user + nice + system;
124        total = active + idle;
125
126        // Read online CPU information.
127        snprintf(file_name, MAX_LENGTH, CPU_ONLINE_FILE_FORMAT, cpu_num);
128        cpu_file = fopen(file_name, "r");
129        online = 0;
130        if (cpu_file == NULL) {
131            ALOGE("%s: failed to open file: %s (%s)", __func__, file_name, strerror(errno));
132            // /sys/devices/system/cpu/cpu0/online is missing on some systems, because cpu0 can't
133            // be offline.
134            online = cpu_num == 0;
135        } else if (1 != fscanf(cpu_file, "%d", &online)) {
136            ALOGE("%s: failed to read CPU online information from file: %s (%s)", __func__,
137                    file_name, strerror(errno));
138            fclose(file);
139            fclose(cpu_file);
140            return errno ? -errno : -EIO;
141        }
142        fclose(cpu_file);
143
144        if (list != NULL) {
145            list[size] = (cpu_usage_t) {
146                .name = CPU_LABEL,
147                .active = active,
148                .total = total,
149                .is_online = online
150            };
151        }
152
153        size++;
154    }
155
156    fclose(file);
157    return size;
158}
159
160static ssize_t get_cooling_devices(thermal_module_t *module, cooling_device_t *list, size_t size) {
161    return 0;
162}
163
164static struct hw_module_methods_t thermal_module_methods = {
165    .open = NULL,
166};
167
168thermal_module_t HAL_MODULE_INFO_SYM = {
169    .common = {
170        .tag = HARDWARE_MODULE_TAG,
171        .module_api_version = THERMAL_HARDWARE_MODULE_API_VERSION_0_1,
172        .hal_api_version = HARDWARE_HAL_API_VERSION,
173        .id = THERMAL_HARDWARE_MODULE_ID,
174        .name = "Default Thermal HAL",
175        .author = "The Android Open Source Project",
176        .methods = &thermal_module_methods,
177    },
178
179    .getTemperatures = get_temperatures,
180    .getCpuUsages = get_cpu_usages,
181    .getCoolingDevices = get_cooling_devices,
182};
183