From: Chanwoo Choi Date: Wed, 19 Feb 2025 03:25:14 +0000 (+0900) Subject: Add hal-backend-service X-Git-Tag: accepted/tizen/unified/20250227.050116~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F03%2F320103%2F9;p=platform%2Fhal%2Fapi%2Fcommon.git Add hal-backend-service Support IPC based HAL communication between hal-api and hal-backend. hal-backend-service is composed with hal-backend-service and hal-backend-service-[module] plugin to separate the common code from specific hal-api TIDL stub (service) code. [Sequence of hal-backend-service execution and hal-backend-service-[module] plugin loading] 1. Tizen booting 2. hal-backend-service.socket is ready on earl booting stage 3. hal-backend-service.service has dependency with hal-backend-service.socket. After executing hal-backend-service.socket, hal-backend-service.service is executed before tizen system services. 4. hal-backend-service initializes IPC server(stub) and loads hal-backend-service-[module] plugin for all HAL modules. 5. hal-backend-service-[module] loads hal-backend under /hal 6. When hal-backend-service receives IPC from hal-ipc-[module]'s proxy, invoke loaded hal-backend function via hal-backend-service-[module] plugin. [Detailed description of hal-backend-service and hal-backend-service-[module] plugin] - hal-backend-service : Provide common code to load hal-backend-service-[module] plugin and create thread for each hal-backend-service-[module] service - hal-backend-servcie-[module] plugin : Implement TIDL stub (server) to communicate hal-api-[module]'s proxy (client) which is executed by hal-[module].h on platform side [Relationship between hal-ipc and hal-backend-service] ------------------------------------------ System Service (Process) | (Function call) hal-ipc (shared library) | | (IPC) | hal-backend-service (Process) | (Function Call) hal-backend-service plugin (shared library) | [Platform] ========================================== | (Function call) [HAL] hal-backend (shared library) ------------------------------------------ kernel (Kernel) Change-Id: Ida817700898957bb20abf7b04b2dd459ea7a9cc7 Signed-off-by: Sangyoun Kwak Signed-off-by: Yunhee Seo Signed-off-by: Chanwoo Choi --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 2409362..aeec522 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,9 +70,15 @@ INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/${PROJECT_NAME}.pc DESTINATION ${LIBDIR}/pkgconfig) +INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/packaging/ DESTINATION lib/systemd/system + FILES_MATCHING + PATTERN "hal-backend-service.*" + ) + ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(tools/lshal) if (${ENABLE_HALCC}) ADD_SUBDIRECTORY(tools/hal-compatibility-checker) endif() +ADD_SUBDIRECTORY(hal-backend-service) diff --git a/hal-backend-service/CMakeLists.txt b/hal-backend-service/CMakeLists.txt new file mode 100644 index 0000000..f2ca559 --- /dev/null +++ b/hal-backend-service/CMakeLists.txt @@ -0,0 +1,35 @@ +PROJECT(hal-backend-service C CXX) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +ADD_DEFINITIONS("-DLOG_TAG=\"HAL_BACKEND_SERVICE\"") + +INCLUDE(FindPkgConfig) +pkg_check_modules(gtest_pkgs REQUIRED + dlog + glib-2.0 + rpc-port + bundle +) + +FOREACH(flag ${gtest_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -fPIC") +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions") +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -Wl,-z,relro") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -lrt") +SET(CMAKE_EXE_LINKER_FLAGS "-pie") + +SET(src + ${CMAKE_SOURCE_DIR}/src/hal-api-conf.c + ${CMAKE_SOURCE_DIR}/hal-backend-service/hal-backend-service.c + ${CMAKE_SOURCE_DIR}/hal-backend-service/thread.c +) +MESSAGE("${PROJECT_NAME}") +ADD_EXECUTABLE(${PROJECT_NAME} ${src}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${gtest_LDFLAGS} ${gtest_pkgs_LDFLAGS} -ldl -L${LIBDIR}/hal) +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin/) diff --git a/hal-backend-service/hal-backend-service.c b/hal-backend-service/hal-backend-service.c new file mode 100644 index 0000000..f9fd2d9 --- /dev/null +++ b/hal-backend-service/hal-backend-service.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2025 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "hal-common.h" +#include "hal-api-conf.h" +#include "common.h" + +#include "thread.h" + +#define RPC_STUB_PROC_NAME "d::HalBackendService" + +struct hal_backend_service_data { + enum hal_module module; + struct thread *thread; + gboolean is_initialized; +}; + +enum hal_backend_service_func { + HAL_BACKEND_SERVICE_FUNC_UNKNOWN = 0, + HAL_BACKEND_SERVICE_FUNC_GET_NAME, + HAL_BACKEND_SERVICE_FUNC_INIT, + HAL_BACKEND_SERVICE_FUNC_EXIT, +}; + +extern char *program_invocation_name; + +static GMainLoop *g_g_main_loop; +static GSList *g_hal_backend_service_data_list = NULL; +G_LOCK_DEFINE_STATIC(hal_backend_service_lock); + +static inline const char* get_backend_service_library_name(struct __hal_module_info *info) +{ + if (!info) + return NULL; + +#if defined(__aarch64__) || defined(__x86_64__) || defined (__riscv) + return info->backend_service_library_name_64bit; +#else + return info->backend_service_library_name; +#endif +} + +static int __open_backend_service(struct __hal_module_info *info) +{ + const char *backend_service_library_name = NULL; + int ret; + + if (info->backend_service && info->backend_service_handle) + return 0; + + backend_service_library_name = get_backend_service_library_name(info); + if (!backend_service_library_name) { + _E("%s: Failed to get backend service library name\n", + info->module_name); + ret = -EINVAL; + goto err; + } else if (access(backend_service_library_name, F_OK) == -1) { + _I("%s: There is no backend service library\n", + info->module_name); + ret = -ENOENT; + goto err; + } + + _I("%s: Prepare to open HAL backend Plugin: library(%s)/count(%d) by %s\n", + info->module_name, get_backend_service_library_name(info), info->backend_service_usage_count, + program_invocation_name); + + if (!info->backend_service_symbol_name) { + _E("%s: Failed to get backend symbol name\n", + info->module_name); + ret = -EINVAL; + goto err; + } + + info->backend_service_handle = dlopen(backend_service_library_name, RTLD_LAZY); + if (!info->backend_service_handle) { + _E("%s: Failed to load backend service library (%s)\n", + info->module_name, dlerror()); + ret = -EINVAL; + goto err; + } + + info->backend_service = dlsym(info->backend_service_handle, + info->backend_service_symbol_name); + if (!info->backend_service) { + _E("%s: Failed to find backend service data (%s)\n", + info->module_name, dlerror()); + ret = -EINVAL; + goto err_dlclose; + } + + return 0; + +err_dlclose: + dlclose(info->backend_service_handle); +err: + info->backend_service = NULL; + info->backend_service_handle = NULL; + + return ret; +} + +static void __close_backend_service(struct __hal_module_info *info) +{ + if (!info->backend_service || !info->backend_service_handle) + return; + + if (info->backend_service_handle) + dlclose(info->backend_service_handle); + + info->backend_service = NULL; + info->backend_service_handle = NULL; +} + +static int __do_backend_service_func(enum hal_module module, + enum hal_backend_service_func func, + void *user_data, + char *name, int name_size) +{ + struct __hal_module_info *info = NULL; + int ret = 0, ret_error = 0, len; + + if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) { + _E("Invalid parameter of HAL module (%d)\n", module); + return 0; + } + + G_LOCK(hal_backend_service_lock); + + if (_hal_api_conf_init()) { + ret = -ENODATA; + 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 = -ENODATA; + goto err_conf_exit; + } + + if (!info->hal_api) { + ret = -ENOENT; + goto err_conf_exit; + } + + if (!info->backend_service_handle || !info->backend_service) { + _E("%s hal-backend-servicd doesn't be opended\n", info->module_name); + ret = -EINVAL; + goto err_conf_exit; + } + + /* Return name of hal_backend_service structure */ + switch (func) { + case HAL_BACKEND_SERVICE_FUNC_GET_NAME: + if (info->backend_service->name== 0 || !name || name_size == 0) { + _E("%s: Invalid size of name[] array\n", info->module_name); + ret = -EINVAL; + goto err_conf_exit; + } + + len = strlen(info->backend_service->name); + if (!info->backend_service->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_service->name, name_size); + break; + case HAL_BACKEND_SERVICE_FUNC_INIT: + if (info->backend_service->early_init) { + ret = info->backend_service->early_init(user_data); + if (ret != 0) { + _E("Failed to early initialize %s: ret(%d)", + info->backend_service->name, ret); + goto err_conf_exit; + } + } + + if (info->backend_service->init) { + ret = info->backend_service->init(user_data); + if (ret != 0) { + _E("Failed to initialize %s: ret(%d)", + info->backend_service->name, ret); + goto err_conf_exit; + } + } + break; + case HAL_BACKEND_SERVICE_FUNC_EXIT: + if (info->backend_service->exit) { + ret = info->backend_service->exit(user_data); + if (ret != 0) { + _W("Cannot exit %s: ret(%d)", + info->backend_service->name, ret); + ret_error = ret; + } + } + + if (info->backend_service->late_exit) { + ret = info->backend_service->late_exit(user_data); + if (ret != 0) { + _W("Cannot late exit %s: ret(%d)", + info->backend_service->name, ret); + ret_error = ret; + } + } + break; + default: + _E("%s: Failed to get backend data\n", info->module_name); + ret = -EINVAL; + goto err_conf_exit; + } + + if (ret_error != 0) + ret = ret_error; + else + ret = 0; + +err_conf_exit: + _hal_api_conf_exit(); +err_unlock: + G_UNLOCK(hal_backend_service_lock); + return ret; +} + +static int __get_backend_service(enum hal_module module, + void **data, void *user_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_backend_service_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_conf_exit; + } + + if (!info->hal_api) { + ret = -ENOENT; + goto err_conf_exit; + } + + ret = __open_backend_service(info); + if (ret < 0) + goto err_conf_exit; + + info->backend_service_usage_count++; + + _I("%s: Get HAL backend Plugin: name(%s)/library(%s)/count(%d) by %s\n", + info->module_name, info->backend_service->name, + get_backend_service_library_name(info), info->backend_service_usage_count, + program_invocation_name); + + G_UNLOCK(hal_backend_service_lock); + return 0; + +err_conf_exit: + _hal_api_conf_exit(); +err: + G_UNLOCK(hal_backend_service_lock); + return ret; +} + +static int __put_backend_service(enum hal_module module, + void *data, void *user_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_backend_service_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->hal_api) { + ret = 0; + goto out; + } + + if (!info->handle || !info->backend) { + _I("%s: Has not yet dlopend backend\n", info->module_name); + ret = 0; + goto out; + } + + if (!info->backend_service_usage_count) { + _I("%s: Already fully put for HAL module\n", info->module_name); + ret = 0; + goto out; + } + + _I("%s: Prepare to exit HAL backend plugin: name(%s)/library(%s)/count(%d) by %s\n", + info->module_name, info->backend_service->name, + get_backend_service_library_name(info), info->backend_service_usage_count, + program_invocation_name); + + info->backend_service_usage_count--; + + _I("%s: Exit and Prepare to put HAL backend: name(%s)/library(%s)/count(%d) by %s\n", + info->module_name, info->backend_service->name, + get_backend_service_library_name(info), info->backend_service_usage_count, + program_invocation_name); + + if (info->backend_service_usage_count > 0) { + ret = 0; + goto out; + } + + __close_backend_service(info); + + _I("%s: Put HAL backend: library(%s)/count(%d) by %s\n", + info->module_name, get_backend_service_library_name(info), info->backend_service_usage_count, + program_invocation_name); + + _hal_api_conf_exit(); + + ret = 0; + +out: + G_UNLOCK(hal_backend_service_lock); + + return ret; +} + +EXPORT +int hal_common_get_backend_service(enum hal_module module, void *data) +{ + return __get_backend_service(module, data, NULL, NULL); +} + +EXPORT +int hal_common_put_backend_service(enum hal_module module, void *data) +{ + return __put_backend_service(module, data, NULL, NULL); +} + +EXPORT +int hal_common_init_backend_service(enum hal_module module, void *user_data) +{ + return __do_backend_service_func(module, HAL_BACKEND_SERVICE_FUNC_INIT, + user_data, NULL, 0); +} + +EXPORT +int hal_common_exit_backend_service(enum hal_module module, void *user_data) +{ + return __do_backend_service_func(module, HAL_BACKEND_SERVICE_FUNC_EXIT, + user_data, NULL, 0); +} + +static int add_hal_backend_service(enum hal_module module) +{ + struct hal_backend_service_data *hal_backend_service_data; + + if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) { + _E("Invalid parameter of HAL module (%d)\n", module); + return -EINVAL; + } + + hal_backend_service_data = g_malloc(sizeof(*hal_backend_service_data)); + if (hal_backend_service_data == NULL) { + _E("Failed to allocate memory for hal_backend_service"); + return -EINVAL; + } + + hal_backend_service_data->module = module; + hal_backend_service_data->thread = NULL; + hal_backend_service_data->is_initialized = FALSE; + + g_hal_backend_service_data_list = g_slist_prepend(g_hal_backend_service_data_list, + (gpointer)hal_backend_service_data); + + return 0; +} + +static gint compare_hal_backend_service( + struct hal_backend_service_data *hal_backend_service_data, + enum hal_module module) +{ + return (hal_backend_service_data->module == module) ? 0 : 1; +} + +static int delete_hal_backend_service( + enum hal_module module) +{ + if (module <= HAL_MODULE_UNKNOWN || module >= HAL_MODULE_END) { + _E("Invalid parameter of HAL module (%d)\n", module); + return -EINVAL; + } + + GSList *slist_hal_backend_service = g_slist_find_custom( + g_hal_backend_service_data_list, (gpointer)module, + (GCompareFunc)compare_hal_backend_service); + if (slist_hal_backend_service == NULL) { + _E("No such hal_backend_service is registered: hal_module(%d)", + module); + return -EINVAL; + } + + g_hal_backend_service_data_list + = g_slist_remove(g_hal_backend_service_data_list, + (gpointer)(slist_hal_backend_service->data)); + + return 0; +} + +static int hal_backend_service_open_and_add_plugin(void) +{ + enum hal_module module; + int ret; + + for (module = HAL_MODULE_UNKNOWN + 1 ; module < HAL_MODULE_END; module++) { + ret = hal_common_get_backend_service(module, NULL); + if (ret == -ENOENT) + continue; + else if (ret < 0) { + _W("Failed to get hal-backend-service\n"); + continue; + } + + ret = add_hal_backend_service(module); + if (ret < 0) { + _W("Failed to add hal-backend-service\n"); + continue; + } + } + + return 0; +} + +static int hal_backend_service_delete_and_close_plugin(void) +{ + enum hal_module module; + int ret; + + for (module = HAL_MODULE_END - 1 ; module > 0; module--) { + ret = delete_hal_backend_service(module); + if (ret < 0) { + _W("Failed to delete hal-backend-service\n"); + continue; + } + + ret = hal_common_put_backend_service(module, NULL); + if (ret < 0) { + _W("Failed to put hal-backend-service\n"); + continue; + } + } + + return 0; +} + +static int hal_backend_service_thread_func(void *data, void **user_data) +{ + struct hal_backend_service_data *service_data = NULL; + int ret = 0; + + if (data == NULL) { + _E("Invalid hal_backend_service(NULL)"); + return THREAD_RETURN_ERROR; + } + service_data = (struct hal_backend_service_data *)data; + + if (service_data->module <= HAL_MODULE_UNKNOWN || service_data->module >= HAL_MODULE_END) { + _E("Invalid parameter of HAL module (%d)\n", service_data->module); + return THREAD_RETURN_ERROR; + } + + ret = hal_common_init_backend_service(service_data->module, NULL); + if (ret < 0) { + _E("Failed to initialize hal backend service: ret(%d)", ret); + return THREAD_RETURN_ERROR; + } + + suspend_thread(service_data->thread); + + return THREAD_RETURN_CONTINUE; +} + +static int hal_backend_thread_finalize_func(void *data) +{ + struct hal_backend_service_data *service_data = NULL; + int ret = 0; + + if (data == NULL) { + _E("Invalid hal_backend_service(NULL)"); + return THREAD_RETURN_ERROR; + } + service_data = (struct hal_backend_service_data *)data; + + if (service_data->module <= HAL_MODULE_UNKNOWN || service_data->module >= HAL_MODULE_END) { + _E("Invalid parameter of HAL module (%d)\n", service_data->module); + return THREAD_RETURN_ERROR; + } + + ret = hal_common_exit_backend_service(service_data->module, NULL); + if (ret < 0) { + _E("Failed to exit hal backend service: ret(%d)", ret); + return THREAD_RETURN_ERROR; + } + + _D("Exit done."); + + return THREAD_RETURN_DONE; +} + +static void create_hal_backend_service_thread(gpointer data, gpointer user_data) +{ + struct hal_backend_service_data *service_data; + size_t *failed_count; + struct thread *thread; + int ret = 0; + + if (data == NULL || user_data == NULL) { + _E("Invalid parameter: data(%p), user_data(%p)", data, user_data); + return; + } + + service_data = (struct hal_backend_service_data *)data; + failed_count = (size_t *)user_data; + + ret = create_daemon_thread(&thread, + hal_backend_service_thread_func, service_data, + hal_backend_thread_finalize_func, service_data); + if (ret < 0) { + _E("Failed to create hal_backend_service thread: ret(%d)", ret); + (*failed_count)++; + return; + } + service_data->thread = thread; + + _D("Thread created for hal_module(%d)", service_data->module); +} + +static void delete_hal_backend_service_thread(gpointer data, gpointer user_data) +{ + struct hal_backend_service_data *service_data; + + if (data == NULL) { + _E("Invalid parameter: data(NULL)"); + return; + } + service_data = (struct hal_backend_service_data *)data; + + destroy_thread(service_data->thread); + service_data->thread = NULL; + + _D("Thread destroyed for hal_module(%d)", service_data->module); +} + +static void hal_backend_service_start(void) +{ + size_t failed_count = 0; + + /* Create threads for hal-backend-service and then tie them */ + g_slist_foreach(g_hal_backend_service_data_list, + create_hal_backend_service_thread, + &failed_count); + if (failed_count != 0) + _W("Cannot register %zu service(s)", failed_count); +} + +static void hal_backend_service_stop(void) +{ + g_slist_foreach(g_hal_backend_service_data_list, + delete_hal_backend_service_thread, NULL); +} + +/** + * Signal Handler + */ +static gboolean handle_signal_glib(gpointer data) +{ + int signal_number = (int)data; + + _D("Received signal(%d)", signal_number); + if (g_g_main_loop != NULL) + g_main_loop_quit(g_g_main_loop); + + return G_SOURCE_REMOVE; +} + +static void handle_signal_std(int signal_number) +{ + _W("Received signal(%d)", signal_number); + raise(SIGTERM); +} + +static void attach_signal_handlers(void) +{ + g_unix_signal_add(SIGINT, handle_signal_glib, (gpointer)SIGINT); + g_unix_signal_add(SIGHUP, handle_signal_glib, (gpointer)SIGHUP); + g_unix_signal_add(SIGTERM, handle_signal_glib, (gpointer)SIGTERM); + + signal(SIGQUIT, handle_signal_std); + signal(SIGABRT, handle_signal_std); +} + +int main(int argc, char **argv) +{ + _D("Initialize hal-backend-service"); + + g_g_main_loop = g_main_loop_new(NULL, FALSE); + + rpc_port_register_proc_info(RPC_STUB_PROC_NAME, NULL); + + /* Load hal-backend-service plugin */ + hal_backend_service_open_and_add_plugin(); + hal_backend_service_start(); + + attach_signal_handlers(); + + g_main_loop_run(g_g_main_loop); + + /* Un-load hal-backend-service plugin */ + hal_backend_service_stop(); + hal_backend_service_delete_and_close_plugin(); + + _D("Exit hal-backend-service"); + + return 0; +} diff --git a/hal-backend-service/thread.c b/hal-backend-service/thread.c new file mode 100644 index 0000000..8bd49b4 --- /dev/null +++ b/hal-backend-service/thread.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2025 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 +#include + +#include "thread.h" + +static void __thread_loop_main(void *_ctx) +{ + enum thread_return ret = THREAD_RETURN_DONE; + enum thread_return finalize_ret = THREAD_RETURN_DONE; + struct thread_context *ctx = _ctx; + void *result = NULL; + + mtx_lock(&ctx->lock); + while (ctx->state != THREAD_STATE_TERMINATED) { + if (ctx->timer.tv_sec || ctx->timer.tv_nsec) { + mtx_unlock(&ctx->lock); + thrd_sleep(&ctx->timer, NULL); + mtx_lock(&ctx->lock); + } + + while (ctx->state == THREAD_STATE_STOPPED) + cnd_wait(&ctx->wait, &ctx->lock); + if (ctx->state == THREAD_STATE_TERMINATED) + break; + + mtx_unlock(&ctx->lock); + ret = ctx->func(ctx->func_arg, &result); + mtx_lock(&ctx->lock); + + if (ret != THREAD_RETURN_CONTINUE) { + ctx->state = THREAD_STATE_TERMINATED; + ctx->result = result; + } + } + mtx_unlock(&ctx->lock); + + if (ctx->finalize != NULL) + finalize_ret = ctx->finalize(ctx->finalize_arg); + + if (finalize_ret != THREAD_RETURN_DONE) + ret = finalize_ret; + + thrd_exit(ret); +} + +static void do_destroy_thread(struct thread *thread) +{ + struct thread_context *ctx = thread->ctx; + + mtx_lock(&ctx->lock); + ctx->state = THREAD_STATE_TERMINATED; + mtx_unlock(&ctx->lock); + + /* try to wake up thread whether it is sleeping or not */ + cnd_signal(&ctx->wait); + + thrd_join(thread->id, NULL); + + cnd_destroy(&ctx->wait); + mtx_destroy(&ctx->lock); +} + +void destroy_thread(struct thread *thread) +{ + do_destroy_thread(thread); + free(thread->ctx); + thread->ctx = NULL; + + free(thread); +} + +static int do_create_thread(struct thread **thread, enum thread_type type, + u_int32_t timer_expire_msec, + int (*func)(void *, void **), void *func_arg, + int (*finalize)(void *), void *finalize_arg) +{ + struct thread *new_thread; + struct thread_context *ctx; + thrd_t tid; + int ret; + + if (!thread || !func) + return -EINVAL; + + new_thread = malloc(sizeof(struct thread)); + if (!new_thread) + return -ENOMEM; + + ctx = calloc(1, sizeof(struct thread_context)); + if (!ctx) { + ret = -ENOMEM; + goto err_malloc; + } + + mtx_init(&ctx->lock, mtx_plain); + cnd_init(&ctx->wait); + + ctx->func = func; + ctx->func_arg = func_arg; + ctx->finalize = finalize; + ctx->finalize_arg = finalize_arg; + + mtx_lock(&ctx->lock); + switch (type) { + case THREAD_TYPE_WORKER: + ctx->state = THREAD_STATE_STOPPED; + break; + case THREAD_TYPE_DAEMON: + ctx->state = THREAD_STATE_RUNNING; + break; + case THREAD_TYPE_TIMER: + ctx->state = THREAD_STATE_RUNNING; + ctx->timer = (struct timespec) { + .tv_sec = timer_expire_msec / 1000, + .tv_nsec = (timer_expire_msec % 1000) * 1000000, + }; + break; + default: + ret = -EINVAL; + goto err; + } + + ret = thrd_create(&tid, (thrd_start_t) __thread_loop_main, (void *)ctx); + if (ret == thrd_error) { + ret = -EINVAL; + goto err; + } + + new_thread->id = tid; + new_thread->ctx = ctx; + + *thread = new_thread; + mtx_unlock(&ctx->lock); + + return 0; + +err: + mtx_unlock(&ctx->lock); + cnd_destroy(&ctx->wait); + mtx_destroy(&ctx->lock); + free(ctx); +err_malloc: + free(new_thread); + + return ret; +} + +int create_daemon_thread(struct thread **thread, + int (*func)(void *, void **), void *func_arg, + int (*finalize)(void *), void *finalize_arg) +{ + return do_create_thread(thread, THREAD_TYPE_DAEMON, 0, func, func_arg, + finalize, finalize_arg); +} + +int create_timer_thread(struct thread **thread, u_int32_t timer_expire_msec, + int (*func)(void *, void **), void *func_arg, + int (*finalize)(void *), void *finalize_arg) +{ + return do_create_thread(thread, THREAD_TYPE_TIMER, timer_expire_msec, + func, func_arg, finalize, finalize_arg); +} + +int create_worker_thread(struct thread **thread, + int (*func)(void *, void **), void *arg, + int (*finalize)(void *), void *finalize_arg) +{ + return do_create_thread(thread, THREAD_TYPE_WORKER, 0, func, arg, + finalize, finalize_arg); +} + +void suspend_thread(struct thread *thread) +{ + struct thread_context *ctx = thread->ctx; + + mtx_lock(&ctx->lock); + ctx->state = THREAD_STATE_STOPPED; + mtx_unlock(&ctx->lock); +} + +void resume_thread(struct thread *thread) +{ + struct thread_context *ctx = thread->ctx; + + mtx_lock(&ctx->lock); + ctx->state = THREAD_STATE_RUNNING; + mtx_unlock(&ctx->lock); + cnd_signal(&ctx->wait); +} + +int wait_for_completion(struct thread *thread, void **result) +{ + struct thread_context *ctx = thread->ctx; + int ret; + + thrd_join(thread->id, &ret); + if (ret == THREAD_RETURN_ERROR) + return ret; + + if (result) + *result = ctx->result; + + cnd_destroy(&ctx->wait); + mtx_destroy(&ctx->lock); + + free(thread->ctx); + thread->ctx = NULL; + + free(thread); + + return 0; +} diff --git a/hal-backend-service/thread.h b/hal-backend-service/thread.h new file mode 100644 index 0000000..bac0970 --- /dev/null +++ b/hal-backend-service/thread.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 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. + */ + +#pragma once + +#include +#include +#include + +enum thread_type { + THREAD_TYPE_WORKER, + THREAD_TYPE_DAEMON, + THREAD_TYPE_TIMER, +}; + +enum thread_state { + THREAD_STATE_RUNNING, + THREAD_STATE_STOPPED, + THREAD_STATE_TERMINATED, +}; + +enum thread_return { + THREAD_RETURN_ERROR = -EXIT_FAILURE, + THREAD_RETURN_DONE = EXIT_SUCCESS, + THREAD_RETURN_CONTINUE, +}; + +struct thread_context { + enum thread_state state; + struct timespec timer; + int (*func)(void *func_arg, void **result); + int (*finalize)(void *finalize_arg); + void *func_arg; + void *result; + void *finalize_arg; + mtx_t lock; + cnd_t wait; +}; + +struct thread { + thrd_t id; + struct thread_context *ctx; +}; + +int create_daemon_thread(struct thread **thread, + int (*func)(void *, void **), void *func_arg, + int (*finalize)(void *), void *finalize_arg); +int create_timer_thread(struct thread **thread, u_int32_t timer_expire_msec, + int (*func)(void *, void **), void *func_arg, + int (*finalize)(void *), void *finalize_arg); +int create_worker_thread(struct thread **thread, + int (*func)(void *, void **), void *func_arg, + int (*finalize)(void *), void *finalize_arg); + +void destroy_thread(struct thread *thread); +void suspend_thread(struct thread *thread); +void resume_thread(struct thread *thread); + +int wait_for_completion(struct thread *thread, void **result); diff --git a/include/hal-common.h b/include/hal-common.h index a9737dc..4f9746c 100644 --- a/include/hal-common.h +++ b/include/hal-common.h @@ -108,6 +108,18 @@ enum hal_common_transport { HAL_COMMON_TRANSPORT_PASSTHROUGH, /**< This indicates dlopen communication way with hal-backend */ }; +/** + * HAL Backend Service + */ +typedef struct __hal_backend_service { + enum hal_module module; + const char *name; + int (*early_init) (void *data); + int (*init) (void *data); + int (*exit) (void *data); + int (*late_exit) (void *data); +} hal_backend_service; + /** * @brief Get the backend library name according to the type of HAL module * @param[in] HAL module id among enum hal_module diff --git a/packaging/hal-api-common.spec b/packaging/hal-api-common.spec index 439a86c..9f311f2 100644 --- a/packaging/hal-api-common.spec +++ b/packaging/hal-api-common.spec @@ -25,6 +25,8 @@ Source7: reboot-haltest Source8: reboot-normal Source9: 500.%{name}.sh Source10: hal-compatibility-checker.conf +Source11: hal-backend-service.socket +Source12: hal-backend-service.service Requires(post): /sbin/ldconfig Requires(postun): /sbin/ldconfig @@ -36,6 +38,8 @@ BuildRequires: pkgconfig(libxml-2.0) BuildRequires: pkgconfig(gmock) BuildRequires: pkgconfig(systemd) BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(rpc-port) +BuildRequires: pkgconfig(bundle) %description %{name} interface @@ -91,6 +95,11 @@ install -D -m 0644 %{SOURCE6} %{buildroot}%{_unitdir}/haltest.target install -D -m 0755 %{SOURCE7} %{buildroot}%{_bindir}/reboot-haltest install -D -m 0755 %{SOURCE8} %{buildroot}%{_bindir}/reboot-normal install -D -m 0755 %{SOURCE9} %{buildroot}%{_datadir}/upgrade/scripts/500.%{name}.sh +#install -D -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/hal-backend-service.socket +#install -D -m 0644 %{SOURCE12} %{buildroot}%{_unitdir}/hal-backend-service.service + +%install_service sockets.target.wants hal-backend-service.socket +%install_service basic.target.wants hal-backend-service.service %clean rm -rf %{buildroot} @@ -119,6 +128,11 @@ rm -f %{_unitdir}/sysinit.target.wants/hal-compatibility-checker.service %{_tmpfilesdir}/hal-compatibility-checker.conf %endif %{_datadir}/upgrade/scripts/500.%{name}.sh +%{_bindir}/hal-backend-service +%{_unitdir}/sockets.target.wants/hal-backend-service.socket +%{_unitdir}/basic.target.wants/hal-backend-service.service +%{_unitdir}/hal-backend-service.socket +%{_unitdir}/hal-backend-service.service %files -n %{devel_name} %defattr(-,root,root,-) diff --git a/packaging/hal-backend-service.service b/packaging/hal-backend-service.service new file mode 100644 index 0000000..5fee38f --- /dev/null +++ b/packaging/hal-backend-service.service @@ -0,0 +1,17 @@ +[Unit] +Description=HAL Backend Service +DefaultDependencies=no +Requires=local-fs.target hal-backend-service.socket +After=local-fs.target hal-backend-service.socket + +[Service] +Type=simple +SmackProcessLabel=System +ExecStart=/usr/bin/hal-backend-service +KillSignal=SIGUSR1 +User=system_fw +Group=system_fw +MemoryMax=20M + +[Install] +WantedBy=basic.target diff --git a/packaging/hal-backend-service.socket b/packaging/hal-backend-service.socket new file mode 100644 index 0000000..e8c21bc --- /dev/null +++ b/packaging/hal-backend-service.socket @@ -0,0 +1,13 @@ +[Unit] +Description=HAL Backend Service Socket +DefaultDependencies=no +Before=sockets.target + +[Socket] +ListenStream=/run/aul/rpcport/.d::HalBackendStub::device_display_funcs +ListenStream=/run/aul/rpcport/.d::HalBackendStub::device_led_funcs +SocketMode=0777 +DirectoryMode=0777 + +[Install] +WantedBy=sockets.target diff --git a/src/common.h b/src/common.h index c09e92a..f0c4cb3 100644 --- a/src/common.h +++ b/src/common.h @@ -104,6 +104,14 @@ struct __hal_module_info { bool hal_api; bool hal_backend_extension; + + /* hal-backend-service plugin */ + int backend_service_usage_count; + char *backend_service_library_name; + char *backend_service_library_name_64bit; + char *backend_service_symbol_name; + void *backend_service_handle; + hal_backend_service *backend_service; }; static inline const char* get_backend_library_name(struct __hal_module_info *info)