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 <ctype.h>
18 #include <errno.h>
19 #include <inttypes.h>
20 #include <stdlib.h>
21 #include <string.h>
22
23 #define LOG_TAG "ThermalHAL"
24 #include <utils/Log.h>
25
26 #include <hardware/hardware.h>
27 #include <hardware/thermal.h>
28
29 #define MAX_LENGTH 50
30
31 #define CPU_USAGE_FILE "/proc/stat"
32 #define TEMPERATURE_FILE_FORMAT "/sys/class/thermal/thermal_zone%d/temp"
33 #define SKIN_TEMPERATURE_FILE "/sys/class/hwmon/hwmon2/device/xo_therm"
34 #define SKIN_TEMPERATURE_FORMAT "Result:%f Raw:%*d\n"
35 #define CPU_ONLINE_FILE_FORMAT "/sys/devices/system/cpu/cpu%d/online"
36
37 #define BATTERY_SENSOR_NUM 1
38 #define GPU_SENSOR_NUM 12
39
40 const int CPU_SENSORS[] = {8, 8, 9, 10, 13, 14};
41
42 #define CPU_NUM (sizeof(CPU_SENSORS) / sizeof(int))
43 #define TEMPERATURE_NUM 9
44
45 // qcom, therm-reset-temp
46 #define CPU_SHUTDOWN_THRESHOLD 115
47 //qcom, limit-temp
48 #define CPU_THROTTLING_THRESHOLD 60
49
50 #define BATTERY_SHUTDOWN_THRESHOLD 60
51 // vendor/lge/bullhead/proprietary/thermal-engine/thermal-engine-8992.conf
52 #define SKIN_THROTTLING_THRESHOLD 37
53
54 #define GPU_LABEL "GPU"
55 #define BATTERY_LABEL "battery"
56 #define SKIN_LABEL "skin"
57
58 const char *CPU_LABEL[] = {"CPU0", "CPU1", "CPU2", "CPU3", "CPU4", "CPU5"};
59
60 /**
61 * Reads device temperature.
62 *
63 * @param file_name Name of file with temperature.
64 * @param temperature_format Format of temperature file.
65 * @param type Device temperature type.
66 * @param name Device temperature name.
67 * @param mult Multiplier used to translate temperature to Celsius.
68 * @param throttling_threshold Throttling threshold for the temperature.
69 * @param shutdown_threshold Shutdown threshold for the temperature.
70 * @param out Pointer to temperature_t structure that will be filled with current values.
71 *
72 * @return 0 on success or negative value -errno on error.
73 */
read_temperature(const char * file_name,const char * temperature_format,int type,const char * name,float mult,float throttling_threshold,float shutdown_threshold,temperature_t * out)74 static ssize_t read_temperature(const char *file_name, const char *temperature_format, int type,
75 const char *name, float mult, float throttling_threshold, float shutdown_threshold,
76 temperature_t *out) {
77 FILE *file;
78 float temp;
79
80 file = fopen(file_name, "r");
81 if (file == NULL) {
82 ALOGE("%s: failed to open: %s", __func__, strerror(errno));
83 return -errno;
84 }
85 if (1 != fscanf(file, temperature_format, &temp)) {
86 fclose(file);
87 ALOGE("%s: failed to read a float: %s", __func__, strerror(errno));
88 return errno ? -errno : -EIO;
89 }
90
91 fclose(file);
92
93 (*out) = (temperature_t) {
94 .type = type,
95 .name = name,
96 .current_value = temp * mult,
97 .throttling_threshold = throttling_threshold,
98 .shutdown_threshold = shutdown_threshold,
99 .vr_throttling_threshold = UNKNOWN_TEMPERATURE
100 };
101
102 return 0;
103 }
104
get_cpu_temperatures(temperature_t * list,size_t size)105 static ssize_t get_cpu_temperatures(temperature_t *list, size_t size) {
106 size_t cpu;
107
108 for (cpu = 0; cpu < CPU_NUM; cpu++) {
109 char file_name[MAX_LENGTH];
110
111 if (cpu >= size) {
112 break;
113 }
114
115 sprintf(file_name, TEMPERATURE_FILE_FORMAT, CPU_SENSORS[cpu]);
116 // tsens_tz_sensor[7, 7, 9, 10, 13, 14]: temperature in Celsius.
117 ssize_t result = read_temperature(file_name, "%f", DEVICE_TEMPERATURE_CPU, CPU_LABEL[cpu],
118 1, CPU_THROTTLING_THRESHOLD, CPU_SHUTDOWN_THRESHOLD, &list[cpu]);
119 if (result != 0) {
120 return result;
121 }
122 }
123 return cpu;
124 }
125
get_temperatures(thermal_module_t * module,temperature_t * list,size_t size)126 static ssize_t get_temperatures(thermal_module_t *module, temperature_t *list, size_t size) {
127 ssize_t result = 0;
128 size_t current_index = 0;
129 char file_name[MAX_LENGTH];
130
131 if (list == NULL) {
132 return TEMPERATURE_NUM;
133 }
134
135 result = get_cpu_temperatures(list, size);
136 if (result < 0) {
137 return result;
138 }
139 current_index += result;
140
141 // GPU temperautre.
142 if (current_index < size) {
143 // tsens_tz_sensor12: temperature in Celsius.
144 sprintf(file_name, TEMPERATURE_FILE_FORMAT, GPU_SENSOR_NUM);
145 result = read_temperature(file_name, "%f", DEVICE_TEMPERATURE_GPU, GPU_LABEL, 1,
146 UNKNOWN_TEMPERATURE, UNKNOWN_TEMPERATURE, &list[current_index]);
147 if (result != 0) {
148 return result;
149 }
150 current_index++;
151 }
152
153 // Battery temperautre.
154 if (current_index < size) {
155 // hwmon sensor: battery: temperature in millidegrees Celsius.
156 sprintf(file_name, TEMPERATURE_FILE_FORMAT, BATTERY_SENSOR_NUM);
157 result = read_temperature(file_name, "%f", DEVICE_TEMPERATURE_BATTERY, BATTERY_LABEL,
158 0.001, UNKNOWN_TEMPERATURE, BATTERY_SHUTDOWN_THRESHOLD, &list[current_index]);
159 if (result != 0) {
160 return result;
161 }
162 current_index++;
163 }
164
165 // Skin temperature.
166 if (current_index < size) {
167 // xo_therm: temperature in Celsius.
168 result = read_temperature(SKIN_TEMPERATURE_FILE, SKIN_TEMPERATURE_FORMAT,
169 DEVICE_TEMPERATURE_SKIN, SKIN_LABEL, 1, SKIN_THROTTLING_THRESHOLD,
170 UNKNOWN_TEMPERATURE, &list[current_index]);
171 if (result != 0) {
172 return result;
173 }
174 current_index++;
175 }
176 return TEMPERATURE_NUM;
177 }
178
get_cpu_usages(thermal_module_t * module,cpu_usage_t * list)179 static ssize_t get_cpu_usages(thermal_module_t *module, cpu_usage_t *list) {
180 int vals, cpu_num, online;
181 ssize_t read;
182 uint64_t user, nice, system, idle, active, total;
183 char *line = NULL;
184 size_t len = 0;
185 size_t size = 0;
186 char file_name[MAX_LENGTH];
187 FILE *file;
188 FILE *cpu_file;
189
190 if (list == NULL) {
191 return CPU_NUM;
192 }
193
194 file = fopen(CPU_USAGE_FILE, "r");
195 if (file == NULL) {
196 ALOGE("%s: failed to open: %s", __func__, strerror(errno));
197 return -errno;
198 }
199
200 while ((read = getline(&line, &len, file)) != -1) {
201 // Skip non "cpu[0-9]" lines.
202 if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) {
203 free(line);
204 line = NULL;
205 len = 0;
206 continue;
207 }
208
209 vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user,
210 &nice, &system, &idle);
211
212 free(line);
213 line = NULL;
214 len = 0;
215
216 if (vals != 5 || size == CPU_NUM) {
217 if (vals != 5) {
218 ALOGE("%s: failed to read CPU information from file: %s", __func__,
219 strerror(errno));
220 } else {
221 ALOGE("/proc/stat file has incorrect format.");
222 }
223 fclose(file);
224 return errno ? -errno : -EIO;
225 }
226
227 active = user + nice + system;
228 total = active + idle;
229
230 // Read online CPU information.
231 snprintf(file_name, MAX_LENGTH, CPU_ONLINE_FILE_FORMAT, cpu_num);
232 cpu_file = fopen(file_name, "r");
233 online = 0;
234 if (cpu_file == NULL) {
235 ALOGE("%s: failed to open file: %s (%s)", __func__, file_name, strerror(errno));
236 fclose(file);
237 return -errno;
238 }
239 if (1 != fscanf(cpu_file, "%d", &online)) {
240 ALOGE("%s: failed to read CPU online information from file: %s (%s)", __func__,
241 file_name, strerror(errno));
242 fclose(file);
243 fclose(cpu_file);
244 return errno ? -errno : -EIO;
245 }
246 fclose(cpu_file);
247
248 list[size] = (cpu_usage_t) {
249 .name = CPU_LABEL[size],
250 .active = active,
251 .total = total,
252 .is_online = online
253 };
254
255 size++;
256 }
257
258 fclose(file);
259
260 if (size != CPU_NUM) {
261 ALOGE("/proc/stat file has incorrect format.");
262 return -EIO;
263 }
264
265 return CPU_NUM;
266 }
267
268 static struct hw_module_methods_t thermal_module_methods = {
269 .open = NULL,
270 };
271
272 thermal_module_t HAL_MODULE_INFO_SYM = {
273 .common = {
274 .tag = HARDWARE_MODULE_TAG,
275 .module_api_version = THERMAL_HARDWARE_MODULE_API_VERSION_0_1,
276 .hal_api_version = HARDWARE_HAL_API_VERSION,
277 .id = THERMAL_HARDWARE_MODULE_ID,
278 .name = "Bullhead Thermal HAL",
279 .author = "The Android Open Source Project",
280 .methods = &thermal_module_methods,
281 },
282 .getTemperatures = get_temperatures,
283 .getCpuUsages = get_cpu_usages,
284 };
285