Add hal-backend-service 03/320103/9
authorChanwoo Choi <cw00.choi@samsung.com>
Wed, 19 Feb 2025 03:25:14 +0000 (12:25 +0900)
committerChanwoo Choi <cw00.choi@samsung.com>
Wed, 26 Feb 2025 08:16:20 +0000 (17:16 +0900)
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 <sy.kwak@samsung.com>
Signed-off-by: Yunhee Seo <yuni.seo@samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
CMakeLists.txt
hal-backend-service/CMakeLists.txt [new file with mode: 0644]
hal-backend-service/hal-backend-service.c [new file with mode: 0644]
hal-backend-service/thread.c [new file with mode: 0644]
hal-backend-service/thread.h [new file with mode: 0644]
include/hal-common.h
packaging/hal-api-common.spec
packaging/hal-backend-service.service [new file with mode: 0644]
packaging/hal-backend-service.socket [new file with mode: 0644]
src/common.h

index 24093620cd72dc48fc7b9dadf4297be0d5c16b49..aeec52217fe95ed0e8c9f6a95151e22f7f1665d4 100644 (file)
@@ -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 (file)
index 0000000..f2ca559
--- /dev/null
@@ -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 (file)
index 0000000..f9fd2d9
--- /dev/null
@@ -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 <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib-unix.h>
+#include <rpc-port-internal.h>
+#include <bundle_internal.h>
+
+#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 (file)
index 0000000..8bd49b4
--- /dev/null
@@ -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 <stdlib.h>
+#include <errno.h>
+
+#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 (file)
index 0000000..bac0970
--- /dev/null
@@ -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 <stdlib.h>
+#include <threads.h>
+#include <time.h>
+
+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);
index a9737dc6ba346adbec26a52998a8eeede72b5615..4f9746cc66e2f953a6d2459081f6f9f07206cde0 100644 (file)
@@ -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
index 439a86cc84a70080c256bd7213317a70fa422b69..9f311f2c8c72149fbdcf783466667ad4946b84d7 100644 (file)
@@ -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 (file)
index 0000000..5fee38f
--- /dev/null
@@ -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 (file)
index 0000000..e8c21bc
--- /dev/null
@@ -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
index c09e92aefc4a0d7ba744aac921ede6c5e964ca4a..f0c4cb3a2dbdf340b0612981aac705ad83461ef3 100644 (file)
@@ -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)