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