/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "VrHALImpl" #include #include #include #include #include #include #include #include #include #include #include #include "vr_int.h" static void *dlhandle; static int (*p_thermal_client_config_query)(char *, struct config_instance **); static int (*p_thermal_client_config_set)(struct config_instance *, unsigned int); static void (*p_thermal_client_config_cleanup)(struct config_instance *, unsigned int); static int max_string_size = 36; static int error_state = 0; //global error state - don't do anything if set! #define DEBUG 0 static thermal_cfg_info_t non_vr_thermal_cfgs; static thermal_cfg_info_t vr_thermal_cfgs; int __attribute__ ((weak)) load_thermal_cfg_info(thermal_cfg_info_t *non_vr, thermal_cfg_info_t *vr) { ALOGD("Entering %s",__func__); non_vr->num_cfgs = 0; vr->num_cfgs = 0; return 0; } /** * Debug function for printing out a log instance */ static void log_config_instance(struct config_instance *instance ){ ALOGI("logging config_instance 0x%p", instance); ALOGI("config_instance: cfg_desc = %s", instance->cfg_desc); ALOGI("config_instance: algo_type = %s", instance->algo_type); ALOGI("config_instance: fields_mask = 0x%x", instance->fields_mask); ALOGI("config_instance: num_fields = %u", instance->num_fields); for (uint32_t i = 0; i < instance->num_fields; i++) { ALOGI("config_instance: field_data[%d]", i); ALOGI("\tfield_name = %s", instance->fields[i].field_name); ALOGI("\tdata_type = %u", instance->fields[i].data_type); ALOGI("\tnum_data = %u", instance->fields[i].num_data); switch (instance->fields[i].data_type){ case FIELD_INT: ALOGI("\tdata = %d", *(int*)(instance->fields[i].data)); break; case FIELD_STR: ALOGI("\tdata = %s", (char*)(instance->fields[i].data)); break; default: ALOGI("\tdata = 0x%p", instance->fields[i].data); break; } } } /** * Debug function for printing out all instances of "ss" and "monitor" algos */ static void query_thermal_config(){ ALOGD("Entering %s",__func__); struct config_instance *instances; int num_configs = (*p_thermal_client_config_query)("ss", &instances); if (num_configs <= 0) { return; } for (int i = 0; i < num_configs; i++) { log_config_instance(&(instances[i])); } if (num_configs > 0) { (*p_thermal_client_config_cleanup)(instances, num_configs); } num_configs = (*p_thermal_client_config_query)("monitor", &instances); if (num_configs <= 0) { return; } for (int i = 0; i < num_configs; i++) { log_config_instance(&(instances[i])); } if (num_configs > 0) { (*p_thermal_client_config_cleanup)(instances, num_configs); } } /** * Load the thermal client library * returns 0 on success */ static int load_thermal_client(void){ ALOGD("Entering %s",__func__); char *thermal_client_so = "vendor/lib64/libthermalclient.so"; dlhandle = dlopen(thermal_client_so, RTLD_NOW | RTLD_LOCAL); if (dlhandle) { dlerror(); p_thermal_client_config_query = (int (*) (char *, struct config_instance **)) dlsym(dlhandle, "thermal_client_config_query"); if (dlerror()) { ALOGE("Unable to load thermal_client_config_query"); goto error_handle; } p_thermal_client_config_set = (int (*) (struct config_instance *, unsigned int)) dlsym(dlhandle, "thermal_client_config_set"); if (dlerror()) { ALOGE("Unable to load thermal_client_config_set"); goto error_handle; } p_thermal_client_config_cleanup = (void (*) (struct config_instance *, unsigned int)) dlsym(dlhandle, "thermal_client_config_cleanup"); if (dlerror()) { ALOGE("Unable to load thermal_client_config_cleanup"); goto error_handle; } } else { ALOGE("unable to open %s", thermal_client_so); return -1; } return 0; error_handle: ALOGE("Error opening functions from %s", thermal_client_so); p_thermal_client_config_query = NULL; p_thermal_client_config_set = NULL; p_thermal_client_config_cleanup = NULL; dlclose(dlhandle); dlhandle = NULL; return -1; } /** * Free the config_instance as allocated in allocate_config_instance */ static void free_config_instance(struct config_instance *config){ if (config) { if (config->fields) { free(config->fields[0].data); free(config->fields[0].field_name); free(config->fields); } free(config->algo_type); free(config->cfg_desc); free(config); } } /** * Allocate a new struct config_instance for modifying the disable field */ static struct config_instance *allocate_config_instance(){ ALOGD("Entering %s",__func__); struct config_instance *config = (struct config_instance *)malloc(sizeof(struct config_instance)); if (!config) { ALOGE("Unable to allocate memory for config"); return NULL; } memset(config, 0, sizeof(*config)); config->cfg_desc = (char *)malloc(sizeof(char) * max_string_size); if (!config->cfg_desc) { free_config_instance(config); ALOGE("Unable to allocate memory for config->cfg_desc"); return NULL; } memset(config->cfg_desc, 0, sizeof(char)*max_string_size); config->algo_type = (char *)malloc(sizeof(char) * max_string_size); if (!config->algo_type) { free_config_instance(config); ALOGE("Unable to allocate memory for config->algo_type"); return NULL; } memset(config->algo_type, 0, sizeof(char) * max_string_size); config->fields = (struct field_data *)malloc(sizeof(struct field_data)); if (!config->fields) { free_config_instance(config); ALOGE("Unable to allocate memory for config->fields"); return NULL; } memset(config->fields, 0, sizeof(*config->fields)); config->fields[0].field_name = (char*)malloc(sizeof(char) * max_string_size); if (!config->fields[0].field_name) { free_config_instance(config); ALOGE("Unable to allocate memory for config->fields[0].field_name"); return NULL; } memset(config->fields[0].field_name, 0, sizeof(char) * max_string_size); config->fields[0].data = (void*)malloc(sizeof(int)); if (!config->fields[0].data) { free_config_instance(config); ALOGE("Unable to allocate memory for config->fields[0].data"); return NULL; } return config; } /** * disable a thermal config * returns 1 on success, anything else is a failure */ static int disable_config(char *config_name, char *algo_type){ ALOGD("Entering %s",__func__); int result = 0; if (error_state) { return 0; } struct config_instance *config = allocate_config_instance(); if (!config) { return 0; } strlcpy(config->cfg_desc, config_name, max_string_size); strlcpy(config->algo_type, algo_type, max_string_size); strlcpy(config->fields[0].field_name, "disable", max_string_size); config->fields_mask |= DISABLE_FIELD; config->num_fields = 1; config->fields[0].data_type = FIELD_INT; config->fields[0].num_data = 1; *(int*)(config->fields[0].data) = 1; //DISABLE result = (*p_thermal_client_config_set)(config, 1); if (DEBUG) { ALOGE("disable profile: name = %s, algo_type = %s, success = %d", config_name, algo_type, result); } free_config_instance(config); return result; } /** * enable a thermal config * returns 1 on success, anything else is failure */ static int enable_config(char *config_name, char *algo_type){ ALOGD("Entering %s",__func__); int result = 0; if (error_state) { return 0; } struct config_instance *config = allocate_config_instance(); if (!config) { return 0; } strlcpy(config->cfg_desc, config_name, max_string_size); strlcpy(config->algo_type, algo_type, max_string_size); strlcpy(config->fields[0].field_name, "disable", max_string_size); config->fields_mask |= DISABLE_FIELD; config->num_fields = 1; config->fields[0].data_type = FIELD_INT; config->fields[0].num_data = 1; *(int*)(config->fields[0].data) = 0; //ENABLE result = (*p_thermal_client_config_set)(config, 1); if (DEBUG) { ALOGE("enable profile: name = %s, algo_type = %s, success = %d", config_name, algo_type, result); } free_config_instance(config); return result; } /** * Call this if there is a compoenent-fatal error * Attempts to clean up any outstanding thermal config state */ static void error_cleanup(){ ALOGD("Entering %s",__func__); //disable VR configs, best-effort so ignore return values for (unsigned int i = 0; i < vr_thermal_cfgs.num_cfgs; i++) { disable_config(vr_thermal_cfgs.cfgs[i].config_name, vr_thermal_cfgs.cfgs[i].algo_name); } // enable non-VR profile, best-effort so ignore return values for (unsigned int i = 0; i < non_vr_thermal_cfgs.num_cfgs; i++) { enable_config(non_vr_thermal_cfgs.cfgs[i].config_name, non_vr_thermal_cfgs.cfgs[i].algo_name); } // set global error flag error_state = 1; } /* * Set global display/GPU/scheduler configuration to used for VR apps. */ static void set_vr_thermal_configuration() { ALOGD("Entering %s",__func__); int result = 1; if (error_state) { return; } //disable non-VR configs for (unsigned int i = 0; i < non_vr_thermal_cfgs.num_cfgs; i++) { result = disable_config(non_vr_thermal_cfgs.cfgs[i].config_name, non_vr_thermal_cfgs.cfgs[i].algo_name); if (result != 1) { goto error; } } //enable VR configs for (unsigned int i = 0; i < vr_thermal_cfgs.num_cfgs; i++) { result = enable_config(vr_thermal_cfgs.cfgs[i].config_name, vr_thermal_cfgs.cfgs[i].algo_name); if (result != 1) { goto error; } } if (DEBUG) { query_thermal_config(); } return; error: error_cleanup(); return; } /* * Reset to default global display/GPU/scheduler configuration. */ static void unset_vr_thermal_configuration() { ALOGD("Entering %s",__func__); int result = 1; if (error_state) { return; } //disable VR configs for (unsigned int i = 0; i < vr_thermal_cfgs.num_cfgs; i++) { result = disable_config(vr_thermal_cfgs.cfgs[i].config_name, vr_thermal_cfgs.cfgs[i].algo_name); if (result != 1) { goto error; } } // enable non-VR profile for (unsigned int i = 0; i < non_vr_thermal_cfgs.num_cfgs; i++) { result = enable_config(non_vr_thermal_cfgs.cfgs[i].config_name, non_vr_thermal_cfgs.cfgs[i].algo_name); if (result != 1) { goto error; } } if (DEBUG) { query_thermal_config(); } return; error: error_cleanup(); return; } static void vr_init(struct vr_module *module){ ALOGD("Entering %s",__func__); int success = load_thermal_client(); if (success != 0) { ALOGE("failed to load thermal client"); error_state = 1; } success = load_thermal_cfg_info(&non_vr_thermal_cfgs, &vr_thermal_cfgs); if (success != 0) { ALOGE("failed to load thermal configs"); error_state = 1; } } static void vr_set_vr_mode(struct vr_module *module, bool enabled){ ALOGD("Entering %s",__func__); if (enabled) { set_vr_thermal_configuration(); } else { unset_vr_thermal_configuration(); } } static struct hw_module_methods_t vr_module_methods = { .open = NULL, // There are no devices for this HAL interface. }; vr_module_t HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = VR_MODULE_API_VERSION_1_0, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = VR_HARDWARE_MODULE_ID, .name = "VR HAL", .author = "Code Aurora Forum", .methods = &vr_module_methods, }, .init = vr_init, .set_vr_mode = vr_set_vr_mode, };