--- /dev/null
+/*
+ * HAL Common API
+ *
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <dirent.h>
+
+#define _GNU_SOURCE
+#include <errno.h>
+
+#include <glib-object.h>
+
+#include "common.h"
+#include "hal-api-conf.h"
+
+extern char *program_invocation_name;
+
+#ifndef EXPORT
+#define EXPORT __attribute__ ((visibility("default")))
+#endif
+
+#define HAL_BACKEND_SYMBOL_NAME 1
+#define HAL_BACKEND_MODULE_NAME 2
+
+static enum hal_abi_version g_platform_curr_abi_version;
+G_LOCK_DEFINE_STATIC(hal_common_lock);
+
+EXPORT
+int hal_common_get_backend_library_name(enum hal_module module, char *name, int size)
+{
+ const char *library_name = NULL;
+ struct __hal_module_info *info = NULL;
+ int ret;
+ int len_library_name;
+
+ /* Check parameter whether is valid or not */
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ if (_hal_api_conf_init())
+ return -EINVAL;
+
+ info = _hal_api_conf_get_module_info(module, NULL);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ library_name = get_backend_library_name(info);
+ if (!library_name) {
+ _E("%s backend library name is NULL\n", info->module_name);
+ ret = 0;
+ goto out;
+ }
+
+ len_library_name = strlen(library_name);
+ if (!name || (len_library_name + 1 > size) || len_library_name == 0) {
+ ret = -EINVAL;
+ name = NULL;
+ goto out;
+ }
+ strncpy(name, library_name, len_library_name);
+ name[len_library_name] = '\0';
+
+ ret = 0;
+out:
+ _hal_api_conf_exit();
+
+ return ret;
+}
+
+static int __hal_common_get_backend_name(enum hal_module module, char *name, int size, int name_type)
+{
+ struct __hal_module_info *info = NULL;
+ char *str = NULL;
+ int ret;
+ int len_str;
+
+ /* Check parameter whether is valid or not */
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid paramer of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ if (_hal_api_conf_init())
+ return -EINVAL;
+
+ info = _hal_api_conf_get_module_info(module, NULL);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (name_type) {
+ case HAL_BACKEND_SYMBOL_NAME:
+ str = info->symbol_name;
+ break;
+ case HAL_BACKEND_MODULE_NAME:
+ str = info->module_name;
+ break;
+ default:
+ break;
+ }
+
+ if (!str) {
+ _I("%s backend name is NULL\n", info->module_name);
+ ret = 0;
+ goto out;
+ }
+
+ len_str = strlen(str);
+ if (!str || (len_str + 1 > size) || len_str == 0) {
+ ret = -EINVAL;
+ str = NULL;
+ goto out;
+ }
+ strncpy(name, str, len_str);
+ name[len_str] = '\0';
+
+ ret = 0;
+out:
+ _hal_api_conf_exit();
+
+ return ret;
+}
+
+EXPORT
+int hal_common_get_backend_symbol_name(enum hal_module module, char *name, int size)
+{
+ return __hal_common_get_backend_name(module, name, size, HAL_BACKEND_SYMBOL_NAME);
+}
+
+EXPORT
+int hal_common_get_backend_module_name(enum hal_module module, char *name, int size)
+{
+ return __hal_common_get_backend_name(module, name, size, HAL_BACKEND_MODULE_NAME);
+}
+
+static int __open_backend(struct __hal_module_info *info)
+{
+ const char *backend_library_name = NULL;
+ int ret;
+
+ if (info->backend && info->handle)
+ return 0;
+
+ backend_library_name = get_backend_library_name(info);
+ if (!backend_library_name) {
+ _E("%s: Failed to get backend library name\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err;
+ } else if (access(backend_library_name, F_OK) == -1) {
+ _I("%s: There is no backend library\n",
+ info->module_name);
+ ret = -ENOENT;
+ goto err;
+ }
+
+ if (!info->symbol_name) {
+ _E("%s: Failed to get backend symbol name\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ info->handle = dlopen(backend_library_name, RTLD_LAZY);
+ if (!info->handle) {
+ _E("%s: Failed to load backend library (%s)\n",
+ info->module_name, dlerror());
+ ret = -EINVAL;
+ goto err;
+ }
+
+ info->backend = dlsym(info->handle, info->symbol_name);
+ if (!info->backend) {
+ _E("%s: Failed to find backend data (%s)\n",
+ info->module_name, dlerror());
+ ret = -EINVAL;
+ goto err_dlclose;
+ }
+
+ _I("%s: Open HAL backend: name(%s)/vendor(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend->name, info->backend->vendor,
+ backend_library_name, info->usage_count,
+ program_invocation_name);
+
+ return 0;
+
+err_dlclose:
+ dlclose(info->handle);
+err:
+ info->backend = NULL;
+ info->handle = NULL;
+
+ return ret;
+}
+
+static void __close_backend(struct __hal_module_info *info)
+{
+ if (!info->backend || !info->handle)
+ return;
+
+ _I("%s: Close HAL backend: name(%s)/vendor(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend->name, info->backend->vendor,
+ get_backend_library_name(info), info->usage_count,
+ program_invocation_name);
+
+ if (info->handle)
+ dlclose(info->handle);
+
+ info->backend = NULL;
+ info->handle = NULL;
+}
+
+static int __init_backend(struct __hal_module_info *info, void **data,
+ const char *library_name)
+{
+ int ret;
+
+ if (!info->handle || !info->backend) {
+ _I("%s: Has not yet dlopend backend\n", info->module_name);
+ return 0;
+ }
+
+ if (!info->backend->init) {
+ _E("%s: hal_backend->init() is NULL\n", info->module_name);
+ return -EINVAL;
+ }
+
+ /* Check HAL ABI Version */
+ ret = hal_common_check_backend_abi_version(info->module,
+ info->backend->abi_version);
+ if (ret < 0) {
+ _E("%s: Failed to check ABI version\n", info->module_name);
+ return -EINVAL;
+ }
+
+ /* Initialize backend */
+ ret = info->backend->init(data);
+ if (ret < 0) {
+ _E("%s: Failed to initialize backend: name(%s)/vendor(%s)\n",
+ info->module_name, info->backend->name,
+ info->backend->vendor);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __exit_backend(struct __hal_module_info *info, void *data,
+ const char *library_name)
+{
+ int ret;
+
+ if (!info->handle || !info->backend) {
+ _I("%s: Has not yet dlopend backend\n", info->module_name);
+ return 0;
+ }
+
+ /* Exit backend */
+ if (info->backend->exit) {
+ ret = info->backend->exit(data);
+ if (ret < 0) {
+ _E("%s: Failed to exit backend: name(%s)/vendor(%s)\n",
+ info->module_name, info->backend->name,
+ info->backend->vendor);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int __get_backend(enum hal_module module, void **data,
+ const char *library_name)
+{
+ struct __hal_module_info *info = NULL;
+ int ret;
+
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ G_LOCK(hal_common_lock);
+ if (_hal_api_conf_init()) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ info = _hal_api_conf_get_module_info(module, library_name);
+ if (info == NULL) {
+ if (!library_name)
+ _E("Failed to get HAL module(%d) information\n", module);
+ else
+ _E("Failed to get HAL module(%d) information (%s)\n",
+ module, library_name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = __open_backend(info);
+ if (ret < 0)
+ goto err;
+
+ ret = __init_backend(info, data, NULL);
+ if (ret < 0) {
+ _E("%s: Failed to initialize the backend library\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err_dlclose;
+ }
+
+ info->usage_count++;
+
+ _I("%s: Get HAL backend: name(%s)/vendor(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend->name, info->backend->vendor,
+ get_backend_library_name(info), info->usage_count,
+ program_invocation_name);
+
+ G_UNLOCK(hal_common_lock);
+ return 0;
+
+err_dlclose:
+ __close_backend(info);
+ _hal_api_conf_exit();
+err:
+ G_UNLOCK(hal_common_lock);
+ return ret;
+}
+
+static int __put_backend(enum hal_module module, void *data,
+ const char *library_name)
+{
+ struct __hal_module_info *info = NULL;
+ int ret;
+
+ /* Check parameter whether is valid or not */
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ G_LOCK(hal_common_lock);
+
+ info = _hal_api_conf_get_module_info(module, library_name);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!info->handle || !info->backend) {
+ _I("%s: Has not yet dlopend backend\n", info->module_name);
+ ret = 0;
+ goto out;
+ }
+
+ if (!info->usage_count) {
+ _I("%s: Already fully put for HAL module\n", info->module_name);
+ ret = 0;
+ goto out;
+ }
+
+ ret = __exit_backend(info, data, NULL);
+ if (ret < 0) {
+ _E("%s: Failed to exit the backend library\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ info->usage_count--;
+
+ _I("%s: Put HAL backend: name(%s)/vendor(%s)/library(%s)/count(%d) by %s\n",
+ info->module_name, info->backend->name, info->backend->vendor,
+ get_backend_library_name(info), info->usage_count,
+ program_invocation_name);
+
+ if (info->usage_count > 0) {
+ ret = 0;
+ goto out;
+ }
+
+ __close_backend(info);
+ _hal_api_conf_exit();
+
+ ret = 0;
+
+out:
+ G_UNLOCK(hal_common_lock);
+ return ret;
+}
+
+static int __get_backend_data(enum hal_module module, unsigned int *abi_version,
+ char *name, int name_size, char *vendor, int vendor_size)
+{
+ struct __hal_module_info *info = NULL;
+ int ret, len;
+
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return 0;
+ }
+
+ G_LOCK(hal_common_lock);
+
+ if (_hal_api_conf_init()) {
+ ret = HAL_ABI_VERSION_UNKNOWN;
+ goto err_unlock;
+ }
+
+ info = _hal_api_conf_get_module_info(module, NULL);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = HAL_ABI_VERSION_UNKNOWN;
+ goto err_conf_exit;
+ }
+
+ ret = __open_backend(info);
+ if (ret < 0)
+ goto err_conf_exit;
+
+ /* Return abi_verion of hal_backend structure */
+ if (!name_size && !vendor_size) {
+ *abi_version = info->backend->abi_version;
+
+ /* Return name of hal_backend structure */
+ } else if (info->backend->name && name_size && !vendor_size) {
+ len = strlen(info->backend->name);
+
+ if (!info->backend->name || (len + 1 > name_size)) {
+ _E("%s: Invalid size of name[] array\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+
+ strncpy(name, info->backend->name, len);
+ name[len] = '\0';
+
+ /* Return vendor of hal_backend structure */
+ } else if (info->backend->vendor && !name_size && vendor_size) {
+ len = strlen(info->backend->vendor);
+
+ if (!info->backend->vendor || (len + 1 > vendor_size)) {
+ _E("%s: Invalid size of vendor[] array\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+
+ strncpy(vendor, info->backend->vendor, len);
+ vendor[len] = '\0';
+ } else {
+ _E("%s: Failed to get backend data\n", info->module_name);
+ ret = -EINVAL;
+ goto err_conf_exit;
+ }
+ ret = 0;
+
+err_conf_exit:
+ _hal_api_conf_exit();
+err_unlock:
+ G_UNLOCK(hal_common_lock);
+ return ret;
+}
+
+EXPORT
+int hal_common_get_backend(enum hal_module module, void **data)
+{
+ return __get_backend(module, data, NULL);
+}
+
+EXPORT
+int hal_common_put_backend(enum hal_module module, void *data)
+{
+ return __put_backend(module, data, NULL);
+}
+
+EXPORT
+int hal_common_get_backend_with_library_name(enum hal_module module,
+ void **data, const char *library_name)
+{
+ return __get_backend(module, data, library_name);
+}
+
+EXPORT
+int hal_common_put_backend_with_library_name(enum hal_module module,
+ void *data, const char *library_name)
+{
+ return __put_backend(module, data, library_name);
+}
+
+EXPORT
+int hal_common_check_backend_abi_version(enum hal_module module,
+ enum hal_abi_version abi_version)
+{
+ struct __hal_module_info *info = NULL;
+ int i;
+ int ret;
+
+ /* Check parameter whether is valid or not */
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid paramer of HAL module(%d)\n", module);
+ return -EINVAL;
+ }
+
+ if (abi_version <= HAL_ABI_VERSION_UNKNOWN
+ || abi_version >= HAL_ABI_VERSION_END) {
+ _E("Invalid paramer of HAL ABI version(%d) for HAL module(%d)\n",
+ abi_version, module);
+ return -EINVAL;
+ }
+
+ if (_hal_api_conf_init())
+ return -EINVAL;
+
+ info = _hal_api_conf_get_module_info(module, NULL);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Check abi_version whether is supported or not */
+ if (!info->hal_api) {
+ _E("%s: Doesn't support HAL API\n", info->module_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!info->num_abi_versions
+ || !info->abi_versions) {
+ _E("%s: Doesn't have the ABI version information\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ g_platform_curr_abi_version = _hal_api_conf_get_platform_abi_version();
+
+ if (g_platform_curr_abi_version <= HAL_ABI_VERSION_UNKNOWN
+ || g_platform_curr_abi_version >= HAL_ABI_VERSION_END) {
+ _E("Invalid paramer of current HAL ABI version(%d)(%d)\n",
+ g_platform_curr_abi_version, module);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < info->num_abi_versions; i++) {
+ struct hal_abi_version_match *data
+ = &info->abi_versions[i];
+
+ if (g_platform_curr_abi_version != data->platform_abi_version)
+ continue;
+
+ if (data->backend_min_abi_version <= HAL_ABI_VERSION_UNKNOWN ||
+ data->backend_min_abi_version >= HAL_ABI_VERSION_END) {
+ _E("%s: abi_versions[%d].backend_min_abi_version(%d) is invalid\n",
+ info->module_name, i, data->backend_min_abi_version);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (abi_version <= data->platform_abi_version
+ && abi_version >= data->backend_min_abi_version) {
+ ret = 0;
+ goto out;
+ }
+
+ _E("%s: \'%s\' doesn't support \'%s\'\n",
+ info->module_name,
+ hal_abi_version_str[g_platform_curr_abi_version],
+ hal_abi_version_str[abi_version]);
+ _E("%s: Must use ABI versions from \'%s\' to \'%s\'\n",
+ info->module_name,
+ hal_abi_version_str[data->backend_min_abi_version],
+ hal_abi_version_str[data->platform_abi_version]);
+ }
+ ret = -EINVAL;
+
+out:
+ _hal_api_conf_exit();
+ return ret;
+}
+
+EXPORT
+unsigned int hal_common_get_backend_abi_version(enum hal_module module)
+{
+ unsigned int abi_version;
+ int ret;
+
+ ret = __get_backend_data(module, &abi_version, NULL, 0, NULL, 0);
+ if (ret < 0)
+ return HAL_ABI_VERSION_UNKNOWN;
+
+ return abi_version;
+}
+
+EXPORT
+int hal_common_get_backend_name(enum hal_module module, char *name, int size)
+{
+ return __get_backend_data(module, NULL, name, size, NULL, 0);
+}
+
+EXPORT
+int hal_common_get_backend_vendor(enum hal_module module, char *vendor, int size)
+{
+ return __get_backend_data(module, NULL, NULL, 0, vendor, size);
+}
+
+
+static int __get_backend_library_data(enum hal_module module,
+ char **lib_names,
+ int lib_count,
+ int lib_name_size)
+{
+ struct __hal_module_info *info = NULL;
+ struct dirent *de;
+ DIR *dir;
+ char *backend_module_name = NULL;
+ int count, i, ret, len;
+#if defined(__aarch64__) || defined(__x86_64__) || defined (__riscv)
+ const char hal_backend_path[] = "/hal/lib64";
+#else
+ const char hal_backend_path[] = "/hal/lib";
+#endif
+
+ /* Check parameter whether is valid or not */
+ if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) {
+ _E("Invalid parameter of HAL module (%d)\n", module);
+ return -EINVAL;
+ }
+
+ if (_hal_api_conf_init())
+ return -EINVAL;
+
+ info = _hal_api_conf_get_module_info(module, NULL);
+ if (info == NULL) {
+ _E("Failed to get HAL module(%d) information\n", module);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (info->backend_module_name == NULL) {
+ _E("Don't support HAL backend of HAL module(%s)\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err;
+ }
+ backend_module_name = g_strdup_printf("libhal-backend-%s",
+ info->backend_module_name);
+ if (!backend_module_name) {
+ _E("Failed to allocate the backend_module_name of HAL module(%s)\n",
+ info->module_name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Find HAL backend libraries */
+ dir = opendir(hal_backend_path);
+ if (!dir) {
+ _E("Failed to find HAL backend path(%s) for HAL module(%s)\n",
+ hal_backend_path, info->module_name);
+ ret = -EINVAL;
+ goto err_free_backend_module_name;
+ }
+
+ count = 0;
+ while ((de = readdir(dir)) != NULL) {
+ if (!g_str_has_prefix(de->d_name, backend_module_name))
+ continue;
+
+ if (lib_count == 0)
+ count++;
+ else if (lib_count > 0) {
+ len = strlen(de->d_name) + 1;
+
+ if (len > lib_name_size)
+ len = lib_name_size;
+
+ strncpy(lib_names[count++], de->d_name, len);
+ }
+ }
+
+ if (lib_count > 0 && count != lib_count) {
+ ret = -EINVAL;
+ goto err_mismatch_count;
+ }
+
+ closedir(dir);
+ _hal_api_conf_exit();
+ g_free(backend_module_name);
+
+ return count;
+
+err_mismatch_count:
+ for (i = count - 1; i >= 0; i--)
+ memset(lib_names[i], 0, strlen(lib_names[i]));
+
+ closedir(dir);
+err_free_backend_module_name:
+ g_free(backend_module_name);
+err:
+ _hal_api_conf_exit();
+ return ret;
+}
+
+EXPORT
+int hal_common_get_backend_count(enum hal_module module)
+{
+ return __get_backend_library_data(module, NULL, 0, 0);
+}
+
+EXPORT
+int hal_common_get_backend_library_names(enum hal_module module,
+ char **library_names,
+ int library_count,
+ int library_name_size)
+{
+ int ret = __get_backend_library_data(module, library_names,
+ library_count, library_name_size);
+ return (ret < 0) ? ret : 0;
+}