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 
get_temperatures(thermal_module_t * module,temperature_t * list,size_t size)39 static 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 
get_cpu_usages(thermal_module_t * module,cpu_usage_t * list)86 static 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         } else {
142             fclose(cpu_file);
143         }
144 
145         if (list != NULL) {
146             list[size] = (cpu_usage_t) {
147                 .name = CPU_LABEL,
148                 .active = active,
149                 .total = total,
150                 .is_online = online
151             };
152         }
153 
154         size++;
155     }
156 
157     fclose(file);
158     return size;
159 }
160 
get_cooling_devices(thermal_module_t * module,cooling_device_t * list,size_t size)161 static ssize_t get_cooling_devices(thermal_module_t *module, cooling_device_t *list, size_t size) {
162     return 0;
163 }
164 
165 static struct hw_module_methods_t thermal_module_methods = {
166     .open = NULL,
167 };
168 
169 thermal_module_t HAL_MODULE_INFO_SYM = {
170     .common = {
171         .tag = HARDWARE_MODULE_TAG,
172         .module_api_version = THERMAL_HARDWARE_MODULE_API_VERSION_0_1,
173         .hal_api_version = HARDWARE_HAL_API_VERSION,
174         .id = THERMAL_HARDWARE_MODULE_ID,
175         .name = "Default Thermal HAL",
176         .author = "The Android Open Source Project",
177         .methods = &thermal_module_methods,
178     },
179 
180     .getTemperatures = get_temperatures,
181     .getCpuUsages = get_cpu_usages,
182     .getCoolingDevices = get_cooling_devices,
183 };
184