halcc: Introduce halcc library 64/303164/13
authorYoungjae Cho <y0.cho@samsung.com>
Tue, 19 Dec 2023 06:24:38 +0000 (15:24 +0900)
committerYoungjae Cho <y0.cho@samsung.com>
Mon, 15 Jan 2024 09:02:35 +0000 (18:02 +0900)
The halcc stands for hal compatibility checker.

Tizen hal-apis can specify what feature and version it expects to
hal-backends in manifest file. The halcc library deserializes and
utilizes the information for checking compatibility between
hal-api and hal-backend.

For example, a hal-api specifies what it needs in xml file:

  | <!--hal-api-xxx.xml-->
  |
  | <manifest version="1.0" type="platform" level="2">
  |     <hal>
  |         <name>AAA</name>
  |         <version>3.1</version>
  |         <transport>passthrough</transport>
  |     </hal>
  |     ...
  | </manifest>

It means that the hal-api expects and requires hal-backend module name
of AAA, and version of 3.x where x >= 1.
(See semantic versioning, https://semver.org)

These are collected and tested by the halcc library.

Currently only the function below is provieded
 - int halcc_check_compatibility(const char *hal_api_manifest_dir,
     halcc_compatibility_cb callback, void *user_data)
   : It collects manifest files at hal_api_manifest_dir and checks
     whether the hal-backends have compatible what hal-apis have
     specified. For each specified <hal> in manifests, callback is
     invoked.

Change-Id: Ibb16c378f6fbe08750b6df7db99ee6626bcfd033
Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
14 files changed:
CMakeLists.txt
halcc/CMakeLists.txt [new file with mode: 0644]
halcc/include/halcc/hal-compatibility-checker.h [new file with mode: 0644]
halcc/src/hal-compatibility-checker.c [new file with mode: 0644]
halcc/src/halcc-object.c [new file with mode: 0644]
halcc/src/halcc-object.h [new file with mode: 0644]
halcc/src/halcc-parser.c [new file with mode: 0644]
halcc/src/halcc-parser.h [new file with mode: 0644]
halcc/src/halcc-util.c [new file with mode: 0644]
halcc/src/halcc-util.h [new file with mode: 0644]
packaging/hal-api-common.pc.in
packaging/hal-api-common.spec
tests/unittest/CMakeLists.txt
tests/unittest/test-hal-compatibility-checker.cc [new file with mode: 0644]

index f88a4f1..6c1e84a 100644 (file)
@@ -61,6 +61,8 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/${PROJECT_NAME}.pc
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/hal-rpmdb-checker.conf
        DESTINATION /usr/lib/tmpfiles.d)
 
+ADD_SUBDIRECTORY(halcc)
+
 ADD_SUBDIRECTORY(tests)
 
 ADD_SUBDIRECTORY(tools/lshal)
diff --git a/halcc/CMakeLists.txt b/halcc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9b427ee
--- /dev/null
@@ -0,0 +1,26 @@
+PROJECT(libhalcc C)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED glib-2.0 libxml-2.0)
+
+FOREACH(flag ${pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
+
+SET(LIBHALCC_SRCS
+       ./src/hal-compatibility-checker.c
+       ./src/halcc-object.c
+       ./src/halcc-util.c
+       ./src/halcc-parser.c)
+
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${LIBHALCC_SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} -ldl hal-api-common)
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-Wno-pointer-sign")
+SET_TARGET_PROPERTIES( ${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR})
+SET_TARGET_PROPERTIES( ${PROJECT_NAME} PROPERTIES VERSION ${VERSION})
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES OUTPUT_NAME halcc)
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${LIBDIR}/hal)
+INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${INCLUDEDIR})
diff --git a/halcc/include/halcc/hal-compatibility-checker.h b/halcc/include/halcc/hal-compatibility-checker.h
new file mode 100644 (file)
index 0000000..414c82a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __HAL_COMPATIBILITY_CHECKER_H__
+#define __HAL_COMPATIBILITY_CHECKER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <hal-common.h>
+
+typedef enum halcc_compatibility_e {
+       HALCC_INCOMPATIBLE,
+       HALCC_COMPATIBLE,
+} halcc_compatibility_e;
+
+typedef void (*halcc_compatibility_cb) (enum hal_module module,
+       halcc_compatibility_e is_compatible, void *user_data);
+
+int halcc_check_compatibility(const char *hal_api_manifest_dir,
+       halcc_compatibility_cb callback, void *user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__HAL_COMPATIBILITY_CHECKER_H__
diff --git a/halcc/src/hal-compatibility-checker.c b/halcc/src/hal-compatibility-checker.c
new file mode 100644 (file)
index 0000000..1ee3f64
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <glib.h>
+
+#include <halcc/hal-compatibility-checker.h>
+#include <hal-common.h>
+
+#include "halcc-object.h"
+#include "halcc-parser.h"
+#include "halcc-util.h"
+
+struct callback_data {
+       halcc_compatibility_cb func;
+       void *user_data;
+};
+
+static int parse_directory(const char *manifest_dir, halcc_manifest *manifest)
+{
+       DIR *dir;
+       int dfd;
+       struct dirent *entry;
+
+       if (!manifest_dir || !manifest)
+               return -EINVAL;
+
+       dir = opendir(manifest_dir);
+       if (!dir) {
+               printf("Failed to opendir '%s', %m\n", manifest_dir);
+               return -errno;
+       }
+
+       dfd = dirfd(dir);
+
+       while ((entry = readdir(dir))) {
+               int fd = -1;
+               int ret;
+
+               if (entry->d_type != DT_REG)
+                       continue;
+
+               fd = openat(dfd, entry->d_name, O_RDONLY);
+               if (fd < 0) {
+                       printf("Failed to openat(), %m\n");
+                       continue;
+               }
+
+               ret = halcc_parse_fd(fd, manifest);
+               if (ret < 0) {
+                       printf("Failed to parse xml, ret=%d\n", ret);
+                       close(fd);
+                       continue;
+               }
+
+               close(fd);
+       }
+
+       closedir(dir);
+
+       return 0;
+}
+
+static halcc_compatibility_e is_compatible(int major, int minor, unsigned int abi_version)
+{
+       if (abi_version == HAL_ABI_VERSION_UNKNOWN)
+               return HALCC_INCOMPATIBLE;
+
+       return HALCC_COMPATIBLE;
+}
+
+static void foreach_hal(void *data, void *user_data)
+{
+       struct callback_data *callback_data = NULL;
+       enum hal_module module;
+       halcc_hal *hal;
+       const char *hal_name = NULL;
+       int major, minor;
+       halcc_compatibility_e compatible = HALCC_INCOMPATIBLE;
+
+       hal = (halcc_hal *) data;
+       callback_data = (struct callback_data *) user_data;
+
+       assert(hal);
+       assert(callback_data);
+       assert(callback_data->func);
+
+       if (halcc_hal_get_name(hal, &hal_name) < 0 || hal_name == NULL)
+               return;
+
+       if (halcc_hal_get_version(hal, &major, &minor, NULL) < 0)
+               return;
+
+       for (module = HAL_MODULE_UNKNOWN; module < HAL_MODULE_END; ++module) {
+               char module_name[128] = { 0 , };
+               unsigned int abi_version;
+               int ret;
+
+               ret = hal_common_get_backend_module_name(module, module_name, sizeof(module_name));
+               if (ret < 0)
+                       continue;
+
+               ret = strncmp(hal_name, module_name, strlen(hal_name) + 1);
+               if (ret != 0)
+                       continue;
+
+               abi_version = hal_common_get_backend_abi_version(module);
+               if (abi_version == HAL_ABI_VERSION_UNKNOWN)
+                       break;
+
+               compatible = is_compatible(major, minor, abi_version);
+               break;
+       }
+
+       if (module >= HAL_MODULE_END) {
+               printf("Unknown hal module: %s\n", hal_name);
+               return;
+       }
+
+       printf("%s: %s\n", hal_name, compatible ? "Compatible" : "Incompatible");
+
+       callback_data->func(module, compatible, callback_data->user_data);
+}
+
+EXPORT
+int halcc_check_compatibility(const char *hal_api_manifest_dir,
+       halcc_compatibility_cb callback, void *user_data)
+{
+       halcc_manifest *manifest = NULL;
+       struct callback_data callback_data = { 0 , };
+       int ret = 0;
+
+       if (!hal_api_manifest_dir)
+               return -EINVAL;
+
+       if (!callback)
+               return -EINVAL;
+
+       ret = halcc_manifest_new(&manifest);
+       if (ret < 0)
+               return ret;
+
+       ret = parse_directory(hal_api_manifest_dir, manifest);
+       if (ret < 0)
+               goto out;
+
+       callback_data = (struct callback_data) { callback, user_data };
+
+       halcc_manifest_foreach_hal(manifest, foreach_hal, &callback_data);
+
+out:
+       if (manifest)
+               halcc_manifest_free(g_steal_pointer(&manifest));
+
+       return ret;
+}
diff --git a/halcc/src/halcc-object.c b/halcc/src/halcc-object.c
new file mode 100644 (file)
index 0000000..96698db
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "halcc-object.h"
+#include "halcc-parser.h"
+#include "halcc-util.h"
+
+#define HALCC_TRANSPORT_DEFAULT                HALCC_TRANSPORT_PASSTHROUGH
+
+typedef struct halcc_version {
+       int major;
+       union {
+               /**
+                * Can be used interchangeably.
+                * min_minor might be useful if max_minor is specified.
+                */
+               int minor;
+               int min_minor;
+       };
+       int max_minor;
+} halcc_version;
+
+typedef struct halcc_interface {
+       char *name;
+       char *instance_id;
+} halcc_interface;
+
+typedef struct halcc_hal {
+       char *name;
+       halcc_version version;
+       halcc_transport_e transport;
+       halcc_dependency_state_e dependency_state;
+       GHashTable *dependencies;
+       GHashTable *interfaces;
+} halcc_hal;
+
+typedef struct halcc_manifest {
+       halcc_manifest_type_e manifest_type;
+       halcc_version version;
+       int level;
+       GHashTable *hals;
+} halcc_manifest;
+
+typedef enum halcc_hash_compare_type_e {
+       HALCC_HASH_COMPARE_TYPE_BACKWARD_MINOR_VERSION,
+       HALCC_HASH_COMPARE_TYPE_EXACT_MINOR_VERSION,
+       HALCC_HASH_COMPARE_TYPE_FORWARD_MINOR_VERSION,
+} halcc_hash_compare_type_e;
+
+typedef struct hash_hal_key {
+       halcc_hal hal;
+       halcc_hash_compare_type_e compare_type;
+} hash_hal_key;
+
+#define HASH_HAL_KEY(_name, _major, _minor, _compare_type)     \
+       (hash_hal_key) {                                        \
+           .hal = {                                    \
+                   .name = (char *) _name,             \
+                   .version.major = _major,            \
+                   .version.minor = _minor,            \
+           },                                          \
+           .compare_type = _compare_type,              \
+       }
+
+#define HASH_HAL_KEY_RAW(_hal, _compare_type)                  \
+       HASH_HAL_KEY((_hal)->name,                              \
+               (_hal)->version.major,                  \
+               (_hal)->version.minor,                  \
+               _compare_type                           \
+       )
+
+static void hashtable_foreach(GHashTable *table, halcc_iter_cb cb, void *user_data)
+{
+       GHashTableIter iter;
+       void *data;
+
+       if (!table || !cb)
+               return;
+
+       g_hash_table_iter_init(&iter, table);
+       while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &data))
+               cb(data, user_data);
+}
+
+static guint hal_hash(gconstpointer key)
+{
+       const halcc_hal *hal = (const halcc_hal *) key;
+
+       assert(hal);
+       assert(hal->name);
+
+       return g_str_hash(hal->name);
+}
+
+static gboolean hal_hash_equal(gconstpointer a, gconstpointer b)
+{
+       const halcc_hal *hal = (const halcc_hal *) a;
+       const hash_hal_key *key = (const hash_hal_key *) b;
+       int compare_type;
+
+       assert(hal);
+       assert(key);
+
+       if (strncmp(hal->name, key->hal.name, strlen(hal->name) + 1) != 0)
+               return false;
+
+       if (hal->version.major != key->hal.version.major)
+               return false;
+
+       compare_type = key->compare_type;
+
+       switch (compare_type) {
+       case HALCC_HASH_COMPARE_TYPE_EXACT_MINOR_VERSION:
+               return hal->version.minor == key->hal.version.minor;
+       case HALCC_HASH_COMPARE_TYPE_BACKWARD_MINOR_VERSION:
+               return hal->version.minor <= key->hal.version.minor;
+       case HALCC_HASH_COMPARE_TYPE_FORWARD_MINOR_VERSION:
+               return hal->version.minor >= key->hal.version.minor;
+       default:
+               break;
+       }
+
+       assert(0); // unreachable
+
+       return false;
+}
+
+static void hal_hash_value_free(gpointer data)
+{
+       halcc_hal *hal = (halcc_hal *) data;
+
+       halcc_hal_free(hal);
+}
+
+static GHashTable* hashtable_hal_new(void)
+{
+       return g_hash_table_new_full(hal_hash, hal_hash_equal, NULL, hal_hash_value_free);
+}
+
+static int hashtable_hal_insert(GHashTable *hal_table, halcc_hal *hal)
+{
+       hash_hal_key key;
+
+       if (!hal_table || !hal)
+               return -EINVAL;
+
+       key = HASH_HAL_KEY_RAW(hal, HALCC_HASH_COMPARE_TYPE_EXACT_MINOR_VERSION);
+
+       if (g_hash_table_lookup_extended(hal_table, &key, NULL, NULL))
+               return -EALREADY;
+
+       g_hash_table_insert(hal_table, hal, hal);
+
+       return 0;
+}
+
+static halcc_hal* hashtable_hal_lookup(GHashTable *hal_table,
+       const char *name, int major, int minor, halcc_hash_compare_type_e type)
+{
+       hash_hal_key key;
+
+       if (!hal_table || !name)
+               return NULL;
+
+       key = HASH_HAL_KEY(name, major, minor, type);
+
+       return g_hash_table_lookup(hal_table, &key);
+}
+
+static halcc_hal* hashtable_hal_steal(GHashTable *hal_table, const char *name, int major, int minor)
+{
+       hash_hal_key key;
+       halcc_hal *hal = NULL;
+
+       if (!hal_table || !name)
+               return NULL;
+
+       key = HASH_HAL_KEY(name, major, minor, HALCC_HASH_COMPARE_TYPE_EXACT_MINOR_VERSION);
+
+       if (!g_hash_table_steal_extended(hal_table, &key, NULL, (gpointer*) &hal))
+               return NULL;
+
+       return hal;
+}
+
+static void hashtable_hal_remove(GHashTable *hal_table, const char *name, int major, int minor)
+{
+       hash_hal_key key;
+
+       if (!hal_table || !name)
+               return;
+
+       key = HASH_HAL_KEY(name, major, minor, HALCC_HASH_COMPARE_TYPE_EXACT_MINOR_VERSION);
+
+       g_hash_table_remove(hal_table, &key);
+}
+
+static guint interface_hash(gconstpointer key)
+{
+       const halcc_interface *interface = (const halcc_interface *) key;
+       char interface_str[HALCC_NAME_MAX * 2] = { 0 , };
+
+       assert(interface);
+       assert(interface->name);
+
+       if (interface->instance_id)
+               snprintf(interface_str, sizeof(interface_str), "%s.%s", interface->name, interface->instance_id);
+       else
+               snprintf(interface_str, sizeof(interface_str), "%s.default", interface->name);
+
+       return g_str_hash(interface_str);
+}
+
+static gboolean interface_hash_equal(gconstpointer a, gconstpointer b)
+{
+       const halcc_interface *interface = (const halcc_interface *) a;
+       const halcc_interface *key = (const halcc_interface *) b;
+
+       assert(interface);
+       assert(key);
+
+       return strncmp(interface->name, key->name, strlen(interface->name) + 1) == 0;
+}
+
+static void interface_hash_value_free(gpointer data)
+{
+       halcc_interface *interface = (halcc_interface *) data;
+
+       halcc_interface_free(interface);
+}
+
+static GHashTable* hashtable_interface_new(void)
+{
+       return g_hash_table_new_full(interface_hash,
+               interface_hash_equal, NULL, interface_hash_value_free);
+}
+
+static int hashtable_interface_insert(GHashTable *interface_table, halcc_interface *interface)
+{
+       g_hash_table_insert(interface_table, interface, interface);
+
+       return 0;
+}
+
+/*
+static halcc_interface* hashtable_interface_lookup(GHashTable *interface_table,
+       const char *name, const char *instance_id)
+{
+       halcc_interface key;
+
+       if (!interface_table || !name)
+               return NULL;
+
+       key = (halcc_interface) {
+               .name = (char *) name,
+               .instance_id = (char *) instance_id,
+       };
+
+       return g_hash_table_lookup(interface_table, &key);
+}
+*/
+
+static void hashtable_interface_remove(GHashTable *interface_table,
+       const char *name, const char *instance_id)
+{
+       halcc_interface key;
+
+       if (!interface_table || !name)
+               return;
+
+       key = (halcc_interface) {
+               .name = (char *) name,
+               .instance_id = (char *) instance_id,
+       };
+
+       g_hash_table_remove(interface_table, &key);
+}
+
+int halcc_manifest_new(halcc_manifest **manifest)
+{
+       halcc_manifest *m;
+
+       if (!manifest)
+               return -EINVAL;
+
+       m = calloc(1, sizeof(halcc_manifest));
+       if (!m)
+               return -ENOMEM;
+
+       *m = (halcc_manifest) {
+               .manifest_type = HALCC_UNINITIALIZED_INT,
+               .version.major = HALCC_UNINITIALIZED_INT,
+               .version.minor = HALCC_UNINITIALIZED_INT,
+               .level = HALCC_UNINITIALIZED_INT,
+               .hals = hashtable_hal_new(),
+       };
+
+       *manifest = g_steal_pointer(&m);
+
+       return 0;
+}
+
+void halcc_manifest_free(halcc_manifest *manifest)
+{
+       if (!manifest)
+               return;
+
+       g_hash_table_destroy(g_steal_pointer(&manifest->hals));
+
+       free(manifest);
+}
+
+int halcc_manifest_set_type(halcc_manifest *manifest, halcc_manifest_type_e type)
+{
+       if (!manifest)
+               return -EINVAL;
+
+       if (type < HALCC_MANIFEST_TYPE_HAL_API || type > HALCC_MANIFEST_TYPE_HAL_BACKEND)
+               return -EINVAL;
+
+
+       return halcc_util_set_int_once(&manifest->manifest_type, type, "manifest.type");
+}
+
+int halcc_manifest_get_type(halcc_manifest *manifest, halcc_manifest_type_e *type)
+{
+       if (!manifest || !type)
+               return -EINVAL;
+
+       *type = manifest->manifest_type;
+
+       return 0;
+}
+
+int halcc_manifest_set_version(halcc_manifest *manifest, int major, int minor)
+{
+       int ret;
+
+       if (!manifest)
+               return -EINVAL;
+
+       if (major < 0 || minor < 0)
+               return -EINVAL;
+
+       ret = halcc_util_set_int_once(&manifest->version.major, major, "manifest.version.major");
+       if (ret != 0)
+               return ret;
+
+       ret = halcc_util_set_int_once(&manifest->version.minor, minor, "manifest.version.minor");
+       if (ret != 0)
+               return ret;
+
+       return 0;
+}
+
+int halcc_manifest_get_version(halcc_manifest *manifest, int *major, int *minor)
+{
+       if (!manifest || !major || !minor)
+               return -EINVAL;
+
+       *major = manifest->version.major;
+       *minor = manifest->version.minor;
+
+       return 0;
+}
+
+int halcc_manifest_set_level(halcc_manifest *manifest, int level)
+{
+       if (!manifest)
+               return -EINVAL;
+
+       return halcc_util_set_int_once(&manifest->level, level, "manifest.level");
+}
+
+int halcc_manifest_get_level(halcc_manifest *manifest, int *level)
+{
+       if (!manifest || !level)
+               return -EINVAL;
+
+       *level = manifest->level;
+
+       return 0;
+}
+
+int halcc_manifest_add_hal(halcc_manifest *manifest, halcc_hal *hal)
+{
+       if (!manifest || !hal)
+               return -EINVAL;
+
+       assert(manifest->hals);
+
+       if (!hal->name)
+               return -EINVAL;
+
+       return hashtable_hal_insert(manifest->hals, hal);
+}
+
+int halcc_manifest_find_hal(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal)
+{
+       halcc_hal *h;
+
+       if (!manifest || !hal_name)
+               return -EINVAL;
+
+       h = hashtable_hal_lookup(manifest->hals,
+               hal_name, major, minor, HALCC_HASH_COMPARE_TYPE_EXACT_MINOR_VERSION);
+       if (!h)
+               return -ENOTSUP;
+
+       if (hal)
+               *hal = g_steal_pointer(&h);
+
+       return 0;
+}
+
+int halcc_manifest_find_hal_raw(halcc_manifest *manifest,
+       halcc_hal *raw, halcc_hal **hal)
+{
+       return halcc_manifest_find_hal(manifest,
+               raw->name, raw->version.major, raw->version.minor, hal);
+}
+
+int halcc_manifest_find_hal_backward_compatible(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal)
+{
+       halcc_hal *h;
+
+       if (!manifest || !hal_name)
+               return -EINVAL;
+
+       h = hashtable_hal_lookup(manifest->hals,
+               hal_name, major, minor, HALCC_HASH_COMPARE_TYPE_BACKWARD_MINOR_VERSION);
+       if (!h)
+               return -ENOTSUP;
+
+       if (hal)
+               *hal = g_steal_pointer(&h);
+
+       return 0;
+}
+
+int halcc_manifest_find_hal_backward_compatible_raw(halcc_manifest *manifest,
+       halcc_hal *raw, halcc_hal **hal)
+{
+       return halcc_manifest_find_hal_backward_compatible(manifest,
+               raw->name, raw->version.major, raw->version.minor, hal);
+}
+
+int halcc_manifest_find_hal_forward_compatible(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal)
+{
+       halcc_hal *h;
+
+       if (!manifest || !hal_name)
+               return -EINVAL;
+
+       h = hashtable_hal_lookup(manifest->hals,
+               hal_name, major, minor, HALCC_HASH_COMPARE_TYPE_FORWARD_MINOR_VERSION);
+       if (!h)
+               return -ENOTSUP;
+
+       if (hal)
+               *hal = g_steal_pointer(&h);
+
+       return 0;
+}
+
+int halcc_manifest_find_hal_forward_compatible_raw(halcc_manifest *manifest,
+       halcc_hal *raw, halcc_hal **hal)
+{
+       return halcc_manifest_find_hal_forward_compatible(manifest,
+               raw->name, raw->version.major, raw->version.minor, hal);
+}
+
+int halcc_manifest_steal_hal(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal)
+{
+       halcc_hal *h = NULL;
+
+       if (!manifest || !hal_name)
+               return -EINVAL;
+
+       h = hashtable_hal_steal(manifest->hals, hal_name, major, minor);
+       if (!h)
+               return -ENOTSUP;
+
+       if (hal)
+               *hal = g_steal_pointer(&h);
+
+       return 0;
+}
+
+void halcc_manifest_remove_hal(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor)
+{
+       if (!manifest || !hal_name)
+               return;
+
+       hashtable_hal_remove(manifest->hals, hal_name, major, minor);
+}
+
+void halcc_manifest_foreach_hal(halcc_manifest *manifest,
+       halcc_iter_cb cb, void *user_data)
+{
+       if (!manifest || !manifest->hals || !cb)
+               return;
+
+       hashtable_foreach(manifest->hals, cb, user_data);
+}
+
+int halcc_hal_new(halcc_hal **hal)
+{
+       halcc_hal *h;
+
+       if (!hal)
+               return -EINVAL;
+
+       h = calloc(1, sizeof(halcc_hal));
+       if (!h)
+               return -ENOMEM;
+
+       h->transport = HALCC_TRANSPORT_DEFAULT;
+       h->dependency_state = HALCC_DEPENDENCY_STATE_NONE;
+       h->dependencies = hashtable_hal_new();
+       h->interfaces = hashtable_interface_new();
+
+       *hal = g_steal_pointer(&h);
+
+       return 0;
+}
+
+void halcc_hal_free(halcc_hal *hal)
+{
+       if (!hal)
+               return;
+
+       free(g_steal_pointer(&hal->name));
+       g_hash_table_destroy(g_steal_pointer(&hal->dependencies));
+       g_hash_table_destroy(g_steal_pointer(&hal->interfaces));
+       free(hal);
+}
+
+int halcc_hal_set_name(halcc_hal *hal, const char *hal_name)
+{
+       char *n;
+
+       if (!hal)
+               return -EINVAL;
+
+       if (hal->name)
+               free(g_steal_pointer(&hal->name));
+
+       if (!hal_name)
+               return 0;
+
+       n = strndup(hal_name, HALCC_NAME_MAX);
+       if (!n)
+               return -ENOMEM;
+
+       hal->name = g_steal_pointer(&n);
+
+       return 0;
+}
+
+int halcc_hal_get_name(halcc_hal *hal, const char **hal_name)
+{
+       if (!hal || !hal_name)
+               return -EINVAL;
+
+       *hal_name = hal->name;
+
+       return 0;
+}
+
+int halcc_hal_set_version(halcc_hal *hal, int major, int min_minor, int max_minor)
+{
+       if (!hal)
+               return -EINVAL;
+
+       hal->version.major = major;
+       hal->version.min_minor = min_minor;
+       hal->version.max_minor = max_minor;
+
+       return 0;
+}
+
+int halcc_hal_get_version(halcc_hal *hal, int *major, int *min_minor, int *max_minor)
+{
+       if (!hal || !major || !min_minor)
+               return -EINVAL;
+
+       *major = hal->version.major;
+       *min_minor = hal->version.min_minor;
+       if (max_minor)
+               *max_minor = hal->version.max_minor;
+
+       return 0;
+}
+
+int halcc_hal_set_transport(halcc_hal *hal, halcc_transport_e transport)
+{
+       if (!hal)
+               return -EINVAL;
+
+       hal->transport = transport;
+
+       return 0;
+}
+
+int halcc_hal_get_transport(halcc_hal *hal, halcc_transport_e *transport)
+{
+       if (!hal || !transport)
+               return -EINVAL;
+
+       assert(hal->transport >= HALCC_TRANSPORT_PASSTHROUGH);
+       assert(hal->transport <= HALCC_TRANSPORT_IPC);
+
+       *transport = hal->transport;
+
+       return 0;
+}
+
+int halcc_hal_add_dependency(halcc_hal *hal, halcc_hal *dependency)
+{
+       if (!hal || !dependency)
+               return -EINVAL;
+
+       return hashtable_hal_insert(hal->dependencies, dependency);
+}
+
+void halcc_hal_remove_dependency(halcc_hal *hal,
+       const char *dependency_hal_name, int major, int minor)
+{
+       if (!hal || !dependency_hal_name)
+               return;
+
+       hashtable_hal_remove(hal->dependencies, dependency_hal_name, major, minor);
+}
+
+void halcc_hal_foreach_dependency(halcc_hal *hal, halcc_iter_cb cb, void *user_data)
+{
+       if (!hal || !hal->dependencies || !cb)
+               return;
+
+       hashtable_foreach(hal->dependencies, cb, user_data);
+}
+
+int halcc_hal_add_interface(halcc_hal *hal, halcc_interface *interface)
+{
+       if (!hal || !interface)
+               return -EINVAL;
+
+       return hashtable_interface_insert(hal->interfaces, interface);
+}
+
+void halcc_hal_remove_interface(halcc_hal *hal,
+       const char *interface_name, const char *instance_id)
+{
+       if (!hal || !interface_name)
+               return;
+
+       hashtable_interface_remove(hal->interfaces, interface_name, instance_id);
+}
+
+void halcc_hal_foreach_interface(halcc_hal *hal, halcc_iter_cb cb, void *user_data)
+{
+       if (!hal || !hal->interfaces || !cb)
+               return;
+
+       hashtable_foreach(hal->interfaces, cb, user_data);
+}
+
+int halcc_interface_new(halcc_interface **interface)
+{
+       halcc_interface *iface;
+
+       if (!interface)
+               return -EINVAL;
+
+       iface = calloc(1, sizeof(halcc_interface));
+       if (!iface)
+               return -ENOMEM;
+
+       *interface = g_steal_pointer(&iface);
+
+       return 0;
+}
+
+void halcc_interface_free(halcc_interface *interface)
+{
+       if (!interface)
+               return;
+
+       free(g_steal_pointer(&interface->name));
+       free(g_steal_pointer(&interface->instance_id));
+       free(g_steal_pointer(&interface));
+}
+
+int halcc_interface_set_name(halcc_interface *interface, const char *interface_name)
+{
+       char *n;
+
+       if (!interface || !interface_name)
+               return -EINVAL;
+
+       n = strndup(interface_name, HALCC_NAME_MAX);
+       if (!n)
+               return -ENOMEM;
+
+       interface->name = g_steal_pointer(&n);
+
+       return 0;
+}
+
+int halcc_interface_get_name(halcc_interface *interface, const char **interface_name)
+{
+       if (!interface || !interface_name)
+               return -EINVAL;
+
+       *interface_name = interface->name;
+
+       return 0;
+}
+
+int halcc_interface_set_instance_id(halcc_interface *interface, const char *instance_id)
+{
+       char *id = NULL;
+
+       if (!interface || !instance_id)
+               return -EINVAL;
+
+       id = strndup(instance_id, HALCC_NAME_MAX);
+       if (!id)
+               return -ENOMEM;
+
+       interface->instance_id = g_steal_pointer(&id);
+
+       return 0;
+}
+
+int halcc_interface_get_instance_id(halcc_interface *interface, const char **instance_id)
+{
+       if (!interface || !instance_id)
+               return -EINVAL;
+
+       *instance_id = interface->instance_id;
+
+       return 0;
+}
diff --git a/halcc/src/halcc-object.h b/halcc/src/halcc-object.h
new file mode 100644 (file)
index 0000000..799b984
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __HALCC_OBJECT_H__
+#define __HALCC_OBJECT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+
+typedef enum halcc_manifest_type_e {
+       HALCC_MANIFEST_TYPE_NONE = 0,
+       HALCC_MANIFEST_TYPE_HAL_API,
+       HALCC_MANIFEST_TYPE_HAL_BACKEND,
+} halcc_manifest_type_e;
+
+typedef enum halcc_transport_e {
+       HALCC_TRANSPORT_NONE = 0,
+       HALCC_TRANSPORT_PASSTHROUGH,
+       HALCC_TRANSPORT_IPC,
+} halcc_transport_e;
+
+typedef enum halcc_dependency_state_e {
+       HALCC_DEPENDENCY_STATE_NONE = 0,
+       HALCC_DEPENDENCY_STATE_VALIDATING,
+       HALCC_DEPENDENCY_STATE_SUCCESS,
+       HALCC_DEPENDENCY_STATE_FAIL,
+} halcc_dependency_state_e;
+
+typedef struct halcc_manifest halcc_manifest;
+typedef struct halcc_hal halcc_hal;
+typedef struct halcc_interface halcc_interface;
+
+typedef void (*halcc_iter_cb) (void *, void *);
+
+int halcc_manifest_new(halcc_manifest **manifest);
+void halcc_manifest_free(halcc_manifest *manifest);
+int halcc_manifest_set_type(halcc_manifest *manifest, halcc_manifest_type_e type);
+int halcc_manifest_get_type(halcc_manifest *manifest, halcc_manifest_type_e *type);
+int halcc_manifest_set_version(halcc_manifest *manifest, int major, int minor);
+int halcc_manifest_get_version(halcc_manifest *manifest, int *major, int *minor);
+int halcc_manifest_set_level(halcc_manifest *manifest, int level);
+int halcc_manifest_get_level(halcc_manifest *manifest, int *level);
+int halcc_manifest_add_hal(halcc_manifest *manifest, halcc_hal *hal);
+int halcc_manifest_find_hal(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal);
+int halcc_manifest_find_hal_raw(halcc_manifest *manifest,
+       halcc_hal *raw, halcc_hal **hal);
+int halcc_manifest_find_hal_backward_compatible(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal);
+int halcc_manifest_find_hal_backward_compatible_raw(halcc_manifest *manifest,
+       halcc_hal *raw, halcc_hal **hal);
+int halcc_manifest_find_hal_forward_compatible(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal);
+int halcc_manifest_find_hal_forward_compatible_raw(halcc_manifest *manifest,
+       halcc_hal *raw, halcc_hal **hal);
+int halcc_manifest_steal_hal(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor, halcc_hal **hal);
+void halcc_manifest_remove_hal(halcc_manifest *manifest,
+       const char *hal_name, int major, int minor);
+void halcc_manifest_foreach_hal(halcc_manifest *manifest,
+       halcc_iter_cb cb, void *user_data);
+
+int halcc_hal_new(halcc_hal **hal);
+void halcc_hal_free(halcc_hal *hal);
+int halcc_hal_set_name(halcc_hal *hal, const char *hal_name);
+int halcc_hal_get_name(halcc_hal *hal, const char **hal_name);
+int halcc_hal_set_version(halcc_hal *hal, int major, int min_minor, int max_minor);
+int halcc_hal_get_version(halcc_hal *hal, int *major, int *min_minor, int *max_minor);
+int halcc_hal_set_transport(halcc_hal *hal, halcc_transport_e transport);
+int halcc_hal_get_transport(halcc_hal *hal, halcc_transport_e *transport);
+int halcc_hal_add_dependency(halcc_hal *hal, halcc_hal *dependency);
+void halcc_hal_remove_dependency(halcc_hal *hal,
+       const char *dependency_hal_name, int major, int minor);
+void halcc_hal_foreach_dependency(halcc_hal *hal, halcc_iter_cb cb, void *user_data);
+int halcc_hal_add_interface(halcc_hal *hal, halcc_interface *interface);
+void halcc_hal_remove_interface(halcc_hal *hal,
+       const char *interface_name, const char *instance_id);
+void halcc_hal_foreach_interface(halcc_hal *hal, halcc_iter_cb cb, void *user_data);
+
+int halcc_interface_new(halcc_interface **interface);
+void halcc_interface_free(halcc_interface *interface);
+int halcc_interface_set_name(halcc_interface *interface, const char *interface_name);
+int halcc_interface_get_name(halcc_interface *interface, const char **interface_name);
+int halcc_interface_set_instance_id(halcc_interface *interface, const char *instance_id);
+int halcc_interface_get_instance_id(halcc_interface *interface, const char **instance_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __HALCC_OBJECT_H__
diff --git a/halcc/src/halcc-parser.c b/halcc/src/halcc-parser.c
new file mode 100644 (file)
index 0000000..4b12c2f
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+
+#include "halcc-object.h"
+#include "halcc-parser.h"
+#include "halcc-util.h"
+
+#define HALCC_DESERIALIZER_VERSION_MAJOR               1
+#define HALCC_DESERIALIZER_VERSION_MINOR               0
+
+#define __xmlchar__ __attribute__((__cleanup__(free_xmlstr)))
+
+static void free_xmlstr(unsigned char **str)
+{
+       xmlFree(*str);
+}
+
+static int parse_interface(xmlNode *node, halcc_interface *interface)
+{
+       assert(node);
+       assert(interface);
+
+       if (!xmlStrEqual(node->name, "interface")) {
+               printf("Invalid interface node, %s\n", node->name);
+               return -EINVAL;
+       }
+
+       for (xmlNode *child = node->children; child; child = child->next) {
+               if (xmlStrEqual(child->name, "name")) {
+                       __xmlchar__ xmlChar *name = xmlNodeGetContent(child);
+                       halcc_interface_set_name(interface, name);
+               } else if (xmlStrEqual(child->name, "instance")) {
+                       __xmlchar__ xmlChar *instance = xmlNodeGetContent(child);
+                       halcc_interface_set_instance_id(interface, (char *) instance);
+               }
+       }
+
+       return 0;
+}
+
+static int parse_hal(xmlNode *node, halcc_hal *hal)
+{
+       int ret = 0;
+
+       assert(node);
+       assert(hal);
+
+       if (!xmlStrEqual(node->name, "hal")) {
+               printf("Invalid hal node, %s\n", node->name);
+               return -EINVAL;
+       }
+
+       for (xmlNode *child = node->children; child; child = child->next) {
+               if (xmlStrEqual(child->name, "name")) {
+                       __xmlchar__ xmlChar *name = xmlNodeGetContent(child);
+
+                       ret = halcc_hal_set_name(hal, name);
+                       if (ret != 0)
+                               printf("Failed to halcc_hal_set_name(), name=%s, ret=%d\n", name, ret);
+               } else if (xmlStrEqual(child->name, "version")) {
+                       int major, min_minor, max_minor;
+                       int scanned;
+                       __xmlchar__ xmlChar *version = xmlNodeGetContent(child);
+
+                       scanned = sscanf(version, "%d.%d-%d", &major, &min_minor, &max_minor);
+                       switch (scanned) {
+                               case 2: // 3.5 is equal to 3.5-5
+                                       max_minor = min_minor;
+                                       ret = halcc_hal_set_version(hal, major, min_minor, max_minor);
+                                       break;
+                               case 3: // 3.5-8
+                                       ret = halcc_hal_set_version(hal, major, min_minor, max_minor);
+                                       break;
+                               default:
+                                       ret = -EINVAL;
+                                       break;
+                       }
+
+                       if (ret != 0)
+                               printf("Failed to halcc_hal_set_version(), ret=%d\n", ret);
+               } else if (xmlStrEqual(child->name, "transport")) {
+                       __xmlchar__ xmlChar *transport = xmlNodeGetContent(child);
+
+                       if (xmlStrEqual(transport, "passthrough"))
+                               ret = halcc_hal_set_transport(hal, HALCC_TRANSPORT_PASSTHROUGH);
+                       else if (xmlStrEqual(transport, "IPC"))
+                               ret = halcc_hal_set_transport(hal, HALCC_TRANSPORT_IPC);
+                       else
+                               ret = -EINVAL;
+
+                       if (ret != 0)
+                               printf("Failed to halcc_hal_set_transport(), %s, ret=%d\n", transport, ret);
+               } else if (xmlStrEqual(child->name, "interface")) {
+                       halcc_interface *iface;
+
+                       ret = halcc_interface_new(&iface);
+                       if (ret != 0) {
+                               printf("Failed to halcc_interface_new(), ret=%d\n", ret);
+                               continue;
+                       }
+
+                       ret = parse_interface(child, iface);
+                       if (ret != 0) {
+                               printf("Failed to parse_interface(), ret=%d\n", ret);
+                               halcc_interface_free(iface);
+                               continue;
+                       }
+
+                       halcc_hal_add_interface(hal, iface);
+                       iface = NULL;
+               } else if (xmlStrEqual(child->name, "dependency")) {
+                       for (xmlNode *grand_child = child->children; grand_child; grand_child = grand_child->next) {
+                               halcc_hal *h;
+
+                               if (!xmlStrEqual(grand_child->name, "hal"))
+                                       continue;
+
+                               ret = halcc_hal_new(&h);
+                               if (ret != 0)
+                                       continue;
+
+                               ret = parse_hal(grand_child, h);
+                               if (ret != 0) {
+                                       halcc_hal_free(h);
+                                       h = NULL;
+                                       continue;
+                               }
+
+                               halcc_hal_add_dependency(hal, h);
+                               h = NULL;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int parse_manifest(xmlNode *node, halcc_manifest *manifest)
+{
+       int ret;
+       xmlChar *prop;
+       int major;
+       int minor;
+       int level;
+
+       assert(node);
+       assert(manifest);
+
+       if (!xmlStrEqual(node->name, "manifest")) {
+               printf("Invalid manifest node, %s\n", node->name);
+               return -EINVAL;
+       }
+
+       // version
+       prop = xmlGetProp(node, "version");
+       if (!prop) {
+               printf("Failed to xmlGetProp() \"version\"\n");
+               return -EINVAL;
+       }
+
+       ret = sscanf(prop, "%d.%d", &major, &minor);
+       xmlFree(prop);
+
+       if (ret != 2) {
+               printf("Failed to scan version, ret=%d\n", ret);
+               return -EINVAL;
+       }
+
+       if (major != HALCC_DESERIALIZER_VERSION_MAJOR || minor != HALCC_DESERIALIZER_VERSION_MINOR) {
+               printf("Manifest scheme doesn't match. Requires manifest version %d.%d\n",
+                       HALCC_DESERIALIZER_VERSION_MAJOR, HALCC_DESERIALIZER_VERSION_MINOR);
+               return -ENOTSUP;
+       }
+
+       ret = halcc_manifest_set_version(manifest, major, minor);
+       if (ret != 0) {
+               printf("Failed to halcc_manifest_set_version(), ret=%d\n", ret);
+               return -EINVAL;
+       }
+
+       // type
+       prop = xmlGetProp(node, "type");
+       if (!prop) {
+               printf("Failed to xmlGetProp() \"type\"\n");
+               return -EINVAL;
+       }
+
+       if (xmlStrEqual(prop, "platform")) {
+               ret = halcc_manifest_set_type(manifest, HALCC_MANIFEST_TYPE_HAL_API);
+               if (ret != 0)
+                       printf("Failed to halcc_manifest_set_type() HALCC_MANIFEST_TYPE_HAL_API, ret=%d\n", ret);
+       } else if (xmlStrEqual(prop, "device")) {
+               ret = halcc_manifest_set_type(manifest, HALCC_MANIFEST_TYPE_HAL_BACKEND);
+               if (ret != 0)
+                       printf("Failed to halcc_manifest_set_type() HALCC_MANIFEST_TYPE_HAL_BACKEND, ret=%d\n", ret);
+       } else {
+               printf("Invalid type property=%s\n", prop);
+               ret = -EINVAL;
+       }
+
+       xmlFree(prop);
+
+       if (ret != 0)
+               return ret;
+
+       // level
+       prop = xmlGetProp(node, "level");
+       if (!prop) {
+               printf("Failed to xmlGetProp() \"level\"\n");
+               return -EINVAL;
+       }
+
+       ret = sscanf(prop, "%d", &level);
+       xmlFree(prop);
+
+       if (ret != 1) {
+               printf("Failed to scan manifest level\n");
+               return -EINVAL;
+       }
+
+       ret = halcc_manifest_set_level(manifest, level);
+       if (ret != 0) {
+               printf("Failed to halcc_manifest_set_level(), ret=%d\n", ret);
+               return ret;
+       }
+
+       for (xmlNode *child = node->children; child; child = child->next) {
+               halcc_hal *h;
+
+               if (!xmlStrEqual(child->name, "hal"))
+                       continue;
+
+               ret = halcc_hal_new(&h);
+               if (ret != 0) {
+                       printf("Failed to halcc_hal_new(), ret=%d\n", ret);
+                       continue;
+               }
+
+               ret = parse_hal(child, h);
+               if (ret != 0) {
+                       printf("Failed to parse_hal(), ret=%d\n", ret);
+                       halcc_hal_free(h);
+                       h = NULL;
+                       continue;
+               }
+
+               halcc_manifest_add_hal(manifest, h);
+               h = NULL;
+       }
+
+       return 0;
+}
+
+
+static int parse_xml_doc(xmlDoc *doc, halcc_manifest *manifest)
+{
+       xmlNode *root = NULL;
+       int ret;
+
+       if (!doc || !manifest)
+               return -EINVAL;
+
+       root = xmlDocGetRootElement(doc);
+       if (!root) {
+               printf("Failed to get root element\n");
+               return -EINVAL;
+       }
+
+       ret = parse_manifest(root, manifest);
+       if (ret != 0) {
+               printf("Failed to parse_manifest(), ret=%d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int halcc_parse_fd(int fd, halcc_manifest *manifest)
+{
+       xmlDoc *doc = NULL;
+       int ret = 0;
+
+       if (!manifest) {
+               printf("Invalid manifest\n");
+               return -EINVAL;
+       }
+
+       doc = xmlReadFd(fd, NULL, NULL, 0);
+       if (!doc) {
+               printf("Failed to read doc %d\n", fd);
+               return -ENOENT;
+       }
+
+       ret = parse_xml_doc(doc, manifest);
+
+       xmlFreeDoc(doc);
+
+       return ret;
+}
+
+int halcc_parse_path(const char *filepath, halcc_manifest *manifest)
+{
+       xmlDoc *doc = NULL;
+       int ret = 0;
+
+       if (!filepath || !manifest) {
+               printf("Invalid filepath or manifest\n");
+               return -EINVAL;
+       }
+
+       doc = xmlReadFile(filepath, NULL, 0);
+       if (!doc) {
+               printf("Failed to read doc %s\n", filepath);
+               return -ENOENT;
+       }
+
+       ret = parse_xml_doc(doc, manifest);
+
+       xmlFreeDoc(doc);
+
+       return ret;
+}
+
+int halcc_parse_string(const char *string, int len, halcc_manifest *manifest)
+{
+       xmlDoc *doc = NULL;
+       int ret = 0;
+
+       if (!string || !manifest) {
+               printf("Invalid string or manifest\n");
+               return -EINVAL;
+       }
+
+       doc = xmlReadMemory(string, len, NULL, NULL, 0);
+       if (!doc) {
+               printf("Failed to read xml string\n");
+               return -ENOENT;
+       }
+
+       ret = parse_xml_doc(doc, manifest);
+
+       xmlFreeDoc(doc);
+
+       return ret;
+}
diff --git a/halcc/src/halcc-parser.h b/halcc/src/halcc-parser.h
new file mode 100644 (file)
index 0000000..4bc8c2b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __HALCC_PARSER_H__
+#define __HALCC_PARSER_H__
+
+#include "halcc-object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int halcc_parse_fd(int fd, halcc_manifest *manifest);
+int halcc_parse_path(const char *filepath, halcc_manifest *manifest);
+int halcc_parse_string(const char *string, int len, halcc_manifest *manifest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __HALCC_PARSER_H__
diff --git a/halcc/src/halcc-util.c b/halcc/src/halcc-util.c
new file mode 100644 (file)
index 0000000..eeb96e8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <errno.h>
+
+#include "halcc-util.h"
+
+int halcc_util_set_int_once(void *buf, int value, const char *buf_desc)
+{
+       int old_value;
+
+       if (!buf)
+               return -EINVAL;
+
+       old_value = *(int *) buf;
+
+       if (old_value != HALCC_UNINITIALIZED_INT && old_value != value) {
+               printf("Cannot modify initialized value, %s, %d to %d\n",
+                       buf_desc ? : "Unknown", old_value, value);
+               return -EALREADY;
+       }
+
+       *(int *) buf = value;
+
+       return 0;
+}
diff --git a/halcc/src/halcc-util.h b/halcc/src/halcc-util.h
new file mode 100644 (file)
index 0000000..80b9f25
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __HALCC_UTIL_H__
+#define __HALCC_UTIL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EXPORT                         __attribute__((visibility("default")))
+#define HALCC_NAME_MAX                 128
+#define HALCC_UNINITIALIZED_INT                (-1)
+
+/**
+ * Modifies buffer pointed by 'buf' to 'value' only when the buffer
+ * has value of HALCC_UNINITIALIZED_INT. Once it has changed to value other
+ * than HALCC_UNINITIALIZED_INT and trying to change the value, the buffer
+ * remains unchanged and this function returns -EALREADY.
+ */
+int halcc_util_set_int_once(void *buf, int value, const char *buf_desc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __HALCC_UTIL_H__
index 782a604..e5bca25 100644 (file)
@@ -4,13 +4,14 @@ package_name=hal-api-common
 prefix=@PREFIX@
 exec_prefix=@EXEC_PREFIX@/hal
 libdir=@LIBDIR@/hal
-includedir=@INCLUDEDIR@/hal
+include_halapi_dir=@INCLUDEDIR@/hal
+include_halcc_dir=@INCLUDEDIR@/halcc
 
 Name: ${package_name}
 Description: ${package_name} interface
 Version: @VERSION@
 
 Requires:
-Libs: -L${libdir} -l${package_name}
-Cflags: -I${includedir}
-CXXflags: -I${includedir}
+Libs: -L${libdir} -l${package_name} -lhalcc
+Cflags: -I${include_halapi_dir} -I${include_halcc_dir}
+CXXflags: -I${include_halapi_dir} -I${include_halcc_dir}
index 058f19c..e728d5f 100644 (file)
@@ -26,6 +26,7 @@ BuildRequires: cmake
 BuildRequires: pkgconfig(dlog)
 BuildRequires: pkgconfig(gio-2.0)
 BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(libxml-2.0)
 BuildRequires: pkgconfig(gmock)
 BuildRequires: pkgconfig(systemd)
 
@@ -112,6 +113,7 @@ systemd-tmpfiles /usr/lib/tmpfiles.d/hal-rpmdb-checker.conf --create
 %files -n %{devel_name}
 %defattr(-,root,root,-)
 %{_includedir}/hal/*.h
+%{_includedir}/halcc/*.h
 %{_libdir}/pkgconfig/*.pc
 %{_sysconfdir}/rpm/macros.hal-api
 
index 90dff51..b2c4e7b 100644 (file)
@@ -1,18 +1,22 @@
 ENABLE_TESTING()
 SET(HAL_API_COMMON_UNITTEST "hal-api-common-unittest")
 
-SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Werror")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Werror -Wno-pointer-sign")
 SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++14 -Wall -Werror")
 
+#INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/halcc/include)
+
 AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/ UNITTEST_SRCS)
-ADD_EXECUTABLE(${HAL_API_COMMON_UNITTEST} ${UNITTEST_SRCS})
+AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/halcc/src/ LIBHALCC_SRCS)
+ADD_EXECUTABLE(${HAL_API_COMMON_UNITTEST} ${UNITTEST_SRCS} ${LIBHALCC_SRCS})
 
 TARGET_INCLUDE_DIRECTORIES(${HAL_API_COMMON_UNITTEST} PUBLIC
-       "${CMAKE_CURRENT_SOURCE_DIR}/../../include"
+       "${CMAKE_SOURCE_DIR}/include"
+       "${CMAKE_SOURCE_DIR}/halcc/include"
 )
 
 INCLUDE(FindPkgConfig)
-pkg_check_modules(hal_api_common_unittest_pkgs REQUIRED gmock)
+pkg_check_modules(hal_api_common_unittest_pkgs REQUIRED gmock glib-2.0 libxml-2.0)
 
 FOREACH(flag ${hal_api_common_unittest_pkgs_CFLAGS})
        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
diff --git a/tests/unittest/test-hal-compatibility-checker.cc b/tests/unittest/test-hal-compatibility-checker.cc
new file mode 100644 (file)
index 0000000..5c1bee6
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <string.h>
+#include <gtest/gtest.h>
+
+#include <halcc/hal-compatibility-checker.h>
+#include "../../halcc/src/halcc-object.h"
+#include "../../halcc/src/halcc-parser.h"
+
+using namespace std;
+
+static char g_manifest_xml[] =
+"<manifest version=\"1.0\" type=\"platform\" level=\"7\">"
+"      <hal>"
+"              <name>hal.device.display</name>"
+"              <version>1.5</version>"
+"              <transport>passthrough</transport>"
+"              <interface>"
+"                      <name>hal_backend_device_display_data</name>"
+"              </interface>"
+"              <dependency>"
+"                      <hal>"
+"                              <name>hal.device.board</name>"
+"                              <version>1.0</version>"
+"                              <transport>passthrough</transport>"
+"                              <interface>"
+"                                      <name>hal_backend_device_board_data</name>"
+"                              </interface>"
+"                      </hal>"
+"                      <hal>"
+"                              <name>hal.tdm</name>"
+"                              <version>1.0</version>"
+"                              <transport>passthrough</transport>"
+"                              <interface>"
+"                                      <name>hal_backend_tdm_data</name>"
+"                              </interface>"
+"                      </hal>"
+"              </dependency>"
+"      </hal>"
+"      <hal>"
+"              <name>hal.something</name>"
+"              <version>2.34</version>"
+"              <transport>passthrough</transport>"
+"              <interface>"
+"                      <name>hal_backend_something_data</name>"
+"              </interface>"
+"      </hal>"
+"      <hal>"
+"              <name>hal.test</name>"
+"              <version>4.8</version>"
+"              <transport>passthrough</transport>"
+"              <interface>"
+"                      <name>hal_backend_test_data</name>"
+"              </interface>"
+"      </hal>"
+"</manifest>";
+
+class HalccObjectTest : public ::testing::Test
+{
+       protected:
+               static void SetUpTestSuite() {
+                       int ret;
+
+                       ret = halcc_manifest_new(&g_manifest);
+                       ASSERT_EQ(ret, 0);
+
+                       ret = halcc_parse_string(g_manifest_xml, sizeof(g_manifest_xml), g_manifest);
+                       ASSERT_EQ(ret, 0);
+
+                       ret = halcc_manifest_find_hal(g_manifest, "hal.test", 4, 8, &g_hal);
+                       ASSERT_EQ(ret, 0);
+               }
+
+               static void TearDownTestSuite() {
+                       halcc_manifest_free(g_manifest);
+                       g_manifest = NULL;
+
+               }
+
+               void SetUp() override {
+
+               }
+
+               void TearDown() override {
+
+               }
+
+               static halcc_manifest *g_manifest;
+               static halcc_hal *g_hal;
+               static halcc_interface *g_interface;
+};
+
+halcc_manifest* HalccObjectTest::g_manifest = NULL;
+halcc_hal* HalccObjectTest::g_hal = NULL;
+halcc_interface* HalccObjectTest::g_interface = NULL;
+
+TEST_F(HalccObjectTest, manifest_get_type)
+{
+       halcc_manifest_type_e type;
+       int ret;
+
+       ret = halcc_manifest_get_type(g_manifest, &type);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(type, HALCC_MANIFEST_TYPE_HAL_API);
+}
+
+TEST_F(HalccObjectTest, manifest_get_version)
+{
+       int major, minor;
+       int ret;
+
+       ret = halcc_manifest_get_version(g_manifest, &major, &minor);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(major, 1);
+       ASSERT_EQ(minor, 0);
+}
+
+TEST_F(HalccObjectTest, manifest_get_level)
+{
+       int level;
+       int ret;
+
+       ret = halcc_manifest_get_level(g_manifest, &level);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(level, 7);
+}
+
+TEST_F(HalccObjectTest, manifest_find_hal_success)
+{
+       halcc_hal *hal = NULL;
+       int ret;
+
+       ret = halcc_manifest_find_hal(g_manifest, "hal.device.display", 1, 5, &hal);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(HalccObjectTest, manifest_find_hal_fail)
+{
+       halcc_hal *hal = NULL;
+       int ret;
+
+       ret = halcc_manifest_find_hal(g_manifest, "hal.device.display", 1, 4, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+
+       ret = halcc_manifest_find_hal(g_manifest, "hal.device.display", 1, 6, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+
+       ret = halcc_manifest_find_hal(g_manifest, "hal.device.display", 2, 4, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+}
+
+TEST_F(HalccObjectTest, manifest_find_hal_backward_compatible_success)
+{
+       halcc_hal *hal = NULL;
+       int ret;
+
+       ret = halcc_manifest_find_hal_backward_compatible(g_manifest, "hal.device.display", 1, 5, &hal);
+       ASSERT_EQ(ret, 0);
+
+       ret = halcc_manifest_find_hal_backward_compatible(g_manifest, "hal.device.display", 1, 6, &hal);
+       ASSERT_EQ(ret, 0);
+
+       ret = halcc_manifest_find_hal_backward_compatible(g_manifest, "hal.device.display", 1, 100, &hal);
+       ASSERT_EQ(ret, 0);
+}
+
+TEST_F(HalccObjectTest, manifest_find_hal_backward_compatible_fail)
+{
+       halcc_hal *hal = NULL;
+       int ret;
+
+       ret = halcc_manifest_find_hal_backward_compatible(g_manifest, "hal.device.display", 1, 4, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+
+       ret = halcc_manifest_find_hal_backward_compatible(g_manifest, "hal.device.display", 2, 4, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+
+       ret = halcc_manifest_find_hal_backward_compatible(g_manifest, "hal.device.display", 2, 5, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+}
+
+TEST_F(HalccObjectTest, manifest_find_hal_steal_success)
+{
+       halcc_hal *hal = NULL;
+       const char *hal_name;
+       int major, minor;
+       int ret;
+
+       ret = halcc_manifest_steal_hal(g_manifest, "hal.something", 2, 34, &hal);
+       ASSERT_EQ(ret, 0);
+
+       ret = halcc_hal_get_name(hal, &hal_name);
+       ASSERT_EQ(ret, 0);
+       ASSERT_STREQ(hal_name, "hal.something");
+
+       ret = halcc_hal_get_version(hal, &major, &minor, NULL);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(major, 2);
+       ASSERT_EQ(minor, 34);
+
+       // Shouldn't be there
+       ret = halcc_manifest_find_hal(g_manifest, "hal.something", 2, 34, &hal);
+       ASSERT_EQ(ret, -ENOTSUP);
+
+       ret = halcc_manifest_add_hal(g_manifest, hal);
+       ASSERT_EQ(ret, 0);
+
+       ret = halcc_manifest_find_hal(g_manifest, "hal.something", 2, 34, &hal);
+       ASSERT_EQ(ret, 0);
+
+       ret = halcc_hal_get_name(hal, &hal_name);
+       ASSERT_EQ(ret, 0);
+       ASSERT_STREQ(hal_name, "hal.something");
+
+       ret = halcc_hal_get_version(hal, &major, &minor, NULL);
+       ASSERT_EQ(ret, 0);
+       ASSERT_EQ(major, 2);
+       ASSERT_EQ(minor, 34);
+}