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