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 #define LOG_TAG "VrHALImpl"
18
19 #include <cutils/log.h>
20
21 #include <dlfcn.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29 #include <hardware/vr.h>
30 #include <hardware/hardware.h>
31
32 #include "thermal_client.h"
33
34
35 static void *dlhandle;
36 static int (*p_thermal_client_config_query)(char *, struct config_instance **);
37 static int (*p_thermal_client_config_set)(struct config_instance *, unsigned int);
38 static void (*p_thermal_client_config_cleanup)(struct config_instance *, unsigned int);
39
40 static int max_string_size = 36;
41 static int error_state = 0; //global error state - don't do anything if set!
42
43 // List thermal configs in format {name, algo_type}
44 // This list is manually synced with the thermal config
45 #define NUM_NON_VR_CONFIGS 4
46 static char *non_vr_thermal_configs[NUM_NON_VR_CONFIGS][2] =
47 {{"SKIN-HIGH-FLOOR", "ss"},
48 {"SKIN-MID-FLOOR", "ss"},
49 {"SKIN-LOW-FLOOR", "ss"},
50 {"VIRTUAL-SS-GPU-SKIN", "ss"}};
51 #define NUM_VR_CONFIGS 1
52 static char *vr_thermal_configs[NUM_VR_CONFIGS][2] =
53 {{"VR-EMMC", "monitor"}};
54
55 #define DEBUG 0
56
57 /**
58 * Debug function for printing out a log instance
59 */
log_config_instance(struct config_instance * instance)60 static void log_config_instance(struct config_instance *instance ){
61 ALOGI("logging config_instance 0x%p", instance);
62 ALOGI("config_instance: cfg_desc = %s", instance->cfg_desc);
63 ALOGI("config_instance: algo_type = %s", instance->algo_type);
64 ALOGI("config_instance: fields_mask = 0x%x", instance->fields_mask);
65 ALOGI("config_instance: num_fields = %u", instance->num_fields);
66 for (uint32_t i = 0; i < instance->num_fields; i++) {
67 ALOGI("config_instance: field_data[%d]", i);
68 ALOGI("\tfield_name = %s", instance->fields[i].field_name);
69 ALOGI("\tdata_type = %u", instance->fields[i].data_type);
70 ALOGI("\tnum_data = %u", instance->fields[i].num_data);
71 switch (instance->fields[i].data_type){
72 case FIELD_INT: ALOGI("\tdata = %d", *(int*)(instance->fields[i].data)); break;
73 case FIELD_STR: ALOGI("\tdata = %s", (char*)(instance->fields[i].data)); break;
74 default: ALOGI("\tdata = 0x%p", instance->fields[i].data); break;
75 }
76 }
77 }
78
79 /**
80 * Debug function for printing out all instances of "ss" and "monitor" algos
81 */
query_thermal_config()82 static void query_thermal_config(){
83 struct config_instance *instances;
84
85 int num_configs = (*p_thermal_client_config_query)("ss", &instances);
86 if (num_configs <= 0) {
87 return;
88 }
89 for (int i = 0; i < num_configs; i++) {
90 log_config_instance(&(instances[i]));
91 }
92 if (num_configs > 0) {
93 (*p_thermal_client_config_cleanup)(instances,num_configs);
94 }
95
96 num_configs = (*p_thermal_client_config_query)("monitor", &instances);
97 if (num_configs <= 0) {
98 return;
99 }
100 for (int i = 0; i < num_configs; i++) {
101 log_config_instance(&(instances[i]));
102 }
103 if (num_configs > 0) {
104 (*p_thermal_client_config_cleanup)(instances,num_configs);
105 }
106 }
107
108 /**
109 * Load the thermal client library
110 * returns 0 on success
111 */
load_thermal_client(void)112 static int load_thermal_client(void)
113 {
114 char *thermal_client_so = "vendor/lib64/libthermalclient.so";
115
116 dlhandle = dlopen(thermal_client_so, RTLD_NOW | RTLD_LOCAL);
117 if (dlhandle) {
118 dlerror();
119 p_thermal_client_config_query = (int (*) (char *, struct config_instance **))
120 dlsym(dlhandle, "thermal_client_config_query");
121 if (dlerror()) {
122 ALOGE("Unable to load thermal_client_config_query");
123 goto error_handle;
124 }
125
126 p_thermal_client_config_set = (int (*) (struct config_instance *, unsigned int))
127 dlsym(dlhandle, "thermal_client_config_set");
128 if (dlerror()) {
129 ALOGE("Unable to load thermal_client_config_set");
130 goto error_handle;
131 }
132
133 p_thermal_client_config_cleanup = (void (*) (struct config_instance *, unsigned int))
134 dlsym(dlhandle, "thermal_client_config_cleanup");
135 if (dlerror()) {
136 ALOGE("Unable to load thermal_client_config_cleanup");
137 goto error_handle;
138 }
139 } else {
140 ALOGE("unable to open %s", thermal_client_so);
141 return -1;
142 }
143
144 return 0;
145
146 error_handle:
147 ALOGE("Error opening functions from %s", thermal_client_so);
148 p_thermal_client_config_query = NULL;
149 p_thermal_client_config_set = NULL;
150 p_thermal_client_config_cleanup = NULL;
151 dlclose(dlhandle);
152 dlhandle = NULL;
153 return -1;
154 }
155
156 /**
157 * Allocate a new struct config_instance for modifying the disable field
158 */
allocate_config_instance()159 static struct config_instance *allocate_config_instance(){
160 struct config_instance *config = (struct config_instance *)malloc(sizeof(struct config_instance));
161 memset(config, 0, sizeof(*config));
162
163 config->cfg_desc = (char *)malloc(sizeof(char)*max_string_size);
164 memset(config->cfg_desc, 0, sizeof(char)*max_string_size);
165
166 config->algo_type = (char *)malloc(sizeof(char)*max_string_size);
167 memset(config->algo_type, 0, sizeof(char) * max_string_size);
168
169 config->fields = (struct field_data *)malloc(sizeof(struct field_data));
170 memset(config->fields, 0, sizeof(*config->fields));
171
172 config->fields[0].field_name = (char*)malloc(sizeof(char)*max_string_size);
173 memset(config->fields[0].field_name, 0, sizeof(char)*max_string_size);
174
175 config->fields[0].data = (void*)malloc(sizeof(int));
176
177 return config;
178 }
179 /**
180 * Free the config_instance as allocated in allocate_config_instance
181 */
free_config_instance(struct config_instance * config)182 static void free_config_instance(struct config_instance *config){
183
184 free(config->fields[0].data);
185 free(config->fields[0].field_name);
186 free(config->fields);
187 free(config->algo_type);
188 free(config->cfg_desc);
189 free(config);
190 }
191
192 /**
193 * disable a thermal config
194 * returns 1 on success, anything else is a failure
195 */
disable_config(char * config_name,char * algo_type)196 static int disable_config(char *config_name, char *algo_type){
197 int result = 0;
198 if (error_state) {
199 return 0;
200 }
201 struct config_instance *config = allocate_config_instance();
202 strlcpy(config->cfg_desc, config_name, max_string_size);
203 strlcpy(config->algo_type, algo_type, max_string_size);
204 strlcpy(config->fields[0].field_name, "disable", max_string_size);
205
206 config->fields_mask |= DISABLE_FIELD;
207 config->num_fields = 1;
208 config->fields[0].data_type = FIELD_INT;
209 config->fields[0].num_data = 1;
210 *(int*)(config->fields[0].data) = 1; //DISABLE
211
212
213 result = (*p_thermal_client_config_set)(config, 1);
214 if (DEBUG) {
215 ALOGE("disable profile: name = %s, algo_type = %s, success = %d", config_name, algo_type, result);
216 }
217 free_config_instance(config);
218
219 return result;
220 }
221
222 /**
223 * enable a thermal config
224 * returns 1 on success, anything else is failure
225 */
enable_config(char * config_name,char * algo_type)226 static int enable_config(char *config_name, char *algo_type){
227 int result = 0;
228 if (error_state) {
229 return 0;
230 }
231 struct config_instance *config = allocate_config_instance();
232 strlcpy(config->cfg_desc, config_name, max_string_size);
233 strlcpy(config->algo_type, algo_type, max_string_size);
234 strlcpy(config->fields[0].field_name, "disable", max_string_size);
235
236 config->fields_mask |= DISABLE_FIELD;
237 config->num_fields = 1;
238 config->fields[0].data_type = FIELD_INT;
239 config->fields[0].num_data = 1;
240 *(int*)(config->fields[0].data) = 0; //ENABLE
241
242 result = (*p_thermal_client_config_set)(config, 1);
243 if (DEBUG) {
244 ALOGE("enable profile: name = %s, algo_type = %s, success = %d",
245 config_name, algo_type, result);
246 }
247
248 free_config_instance(config);
249
250 return result;
251 }
252
253 /**
254 * Call this if there is a compoenent-fatal error
255 * Attempts to clean up any outstanding thermal config state
256 */
error_cleanup()257 static void error_cleanup(){
258 //disable VR configs, best-effort so ignore return values
259 for (unsigned int i = 0; i < NUM_VR_CONFIGS; i++) {
260 disable_config(vr_thermal_configs[i][0], vr_thermal_configs[i][1]);
261 }
262
263 // enable non-VR profile, best-effort so ignore return values
264 for (unsigned int i = 0; i < NUM_NON_VR_CONFIGS; i++) {
265 enable_config(non_vr_thermal_configs[i][0], non_vr_thermal_configs[i][1]);
266 }
267
268 // set global error flag
269 error_state = 1;
270 }
271
272 /*
273 * Set global display/GPU/scheduler configuration to used for VR apps.
274 */
set_vr_thermal_configuration()275 static void set_vr_thermal_configuration() {
276 int result = 1;
277 if (error_state) {
278 return;
279 }
280
281 //disable non-VR configs
282 for (unsigned int i = 0; i < NUM_NON_VR_CONFIGS; i++) {
283 result = disable_config(non_vr_thermal_configs[i][0], non_vr_thermal_configs[i][1]);
284 if (result != 1) {
285 goto error;
286 }
287 }
288
289 //enable VR configs
290 for (unsigned int i = 0; i < NUM_VR_CONFIGS; i++) {
291 result = enable_config(vr_thermal_configs[i][0], vr_thermal_configs[i][1]);
292 if (result != 1) {
293 goto error;
294 }
295 }
296
297 if (DEBUG) {
298 query_thermal_config();
299 }
300
301 return;
302
303 error:
304 error_cleanup();
305 return;
306 }
307
308 /*
309 * Reset to default global display/GPU/scheduler configuration.
310 */
unset_vr_thermal_configuration()311 static void unset_vr_thermal_configuration() {
312 int result = 1;
313 if (error_state) {
314 return;
315 }
316
317 //disable VR configs
318 for (unsigned int i = 0; i < NUM_VR_CONFIGS; i++) {
319 result = disable_config(vr_thermal_configs[i][0], vr_thermal_configs[i][1]);
320 if (result != 1) {
321 goto error;
322 }
323 }
324
325 // enable non-VR profile
326 for (unsigned int i = 0; i < NUM_NON_VR_CONFIGS; i++) {
327 result = enable_config(non_vr_thermal_configs[i][0], non_vr_thermal_configs[i][1]);
328 if (result != 1) {
329 goto error;
330 }
331 }
332
333 if (DEBUG) {
334 query_thermal_config();
335 }
336
337 return;
338
339 error:
340 error_cleanup();
341 return;
342 }
343
vr_init(struct vr_module * module)344 static void vr_init(struct vr_module *module) {
345 int success = load_thermal_client();
346 if (success != 0) {
347 ALOGE("failed to load thermal client");
348 error_state = 1;
349 }
350 }
351
vr_set_vr_mode(struct vr_module * module,bool enabled)352 static void vr_set_vr_mode(struct vr_module *module, bool enabled) {
353 if (enabled) {
354 set_vr_thermal_configuration();
355 } else {
356 unset_vr_thermal_configuration();
357 }
358 }
359
360 static struct hw_module_methods_t vr_module_methods = {
361 .open = NULL, // There are no devices for this HAL interface.
362 };
363
364
365 vr_module_t HAL_MODULE_INFO_SYM = {
366 .common = {
367 .tag = HARDWARE_MODULE_TAG,
368 .module_api_version = VR_MODULE_API_VERSION_1_0,
369 .hal_api_version = HARDWARE_HAL_API_VERSION,
370 .id = VR_HARDWARE_MODULE_ID,
371 .name = "Marlin / Sailfish VR HAL",
372 .author = "The Android Open Source Project",
373 .methods = &vr_module_methods,
374 },
375
376 .init = vr_init,
377 .set_vr_mode = vr_set_vr_mode,
378 };
379