[VD/NPUMGR] Add the TRIV2 plugin prototype of npumgr
authorDongju Chae <dongju.chae@samsung.com>
Thu, 6 May 2021 04:43:10 +0000 (13:43 +0900)
committer파리차이카푸르/On-Device Lab(SR)/Engineer/삼성전자 <pk.kapoor@samsung.com>
Tue, 18 May 2021 07:08:46 +0000 (16:08 +0900)
This patch adds the TRIV2 plugin prototype of npumgr.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
tests/apptests/npumgr/meson.build
tests/apptests/npumgr/npumgr.cc
tests/apptests/npumgr/npumgr_api.cc
tests/apptests/npumgr/npumgr_api.h
tests/apptests/npumgr/npumgr_device.cc [new file with mode: 0644]
tests/apptests/npumgr/npumgr_test.cc
tests/apptests/npumgr/npumgr_triv2.cc [new file with mode: 0644]
tests/apptests/npumgr/npumgr_triv2.h [new file with mode: 0644]

index 38ec772..6c1a38c 100644 (file)
@@ -1,9 +1,11 @@
 # Note that VD NPU Manager is a gdbus-based thread application
 glib_dep = dependency('glib-2.0', required: false)
+gmodule_dep = dependency('gmodule-2.0', required: false)
 giounix_dep = dependency('gio-unix-2.0', required: false)
 if glib_dep.found() and giounix_dep.found()
   npumgr_deps = [
     glib_dep,
+    gmodule_dep,
     giounix_dep
   ]
 
@@ -16,6 +18,17 @@ if glib_dep.found() and giounix_dep.found()
     install_dir : ne_libdir
   )
 
+  npumgr_triv2_lib = shared_library ('npumgr_triv2',
+    ['npumgr_triv2.cc', 'npumgr_device.cc'],
+    include_directories : [ne_common_inc, ne_host_inc],
+    dependencies: npumgr_deps,
+    link_with : ne_library_shared,
+    build_rpath : ne_libdir,
+    install : true,
+    install_rpath : ne_libdir,
+    install_dir : ne_libdir
+  )
+
   executable ('apptest_npumgr',
     'npumgr_test.cc',
     link_with : npumgr_lib,
@@ -25,7 +38,7 @@ if glib_dep.found() and giounix_dep.found()
   )
 
   executable ('dummy_npumgr',
-    'npumgr.cc',
+    ['npumgr.cc', 'npumgr_device.cc'],
     dependencies: npumgr_deps,
     install : true,
     install_rpath : ne_libdir,
index f6d19a7..1a5108a 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 #include <cstdint>
+#include <vector>
+#include <string>
 
+#include <gmodule.h>
 #include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
+#include <npumgr_api.h>
+#include <npumgr_device.h>
+
+#define NPUMGR_DEVICE_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), NPUMGR_TYPE_DEVICE, NpumgrDeviceClass))
 
 /**
- * @brief Class for NPU Manager Context
+ * @brief Internal data structure for NPU Manager Context
  */
-class npumgrContext {
- public:
-  npumgrContext () : device_id_ (-1), priority_ (-1) {}
+typedef struct {
+  NpumgrDevice *device;
+  npumgr_context handle;
+} NpumgrContext;
 
-  /** @brief set device id */
-  void setDeviceID (int device_id) { device_id_ = device_id; }
-  /** @brief set priority */
-  void setPriority (int priority) { priority_ = priority; }
-
- private:
-  int device_id_;
-  int priority_;
-};
+G_LOCK_DEFINE_STATIC (mutex);
+static GHashTable *ctx_table;
 
 static GDBusNodeInfo *introspection_data = NULL;
 /* Introspection data for the npumgr service (NYI) */
 static const gchar introspection_xml[] =
     " <node>"
     "   <interface name='sr.odl.NPUManager.API'>"
+    "     <method name='DeviceGetAvailableList'>"
+    "       <arg type='u' name='num_devices' direction='out'/>"
+    "       <arg type='ai' name='device_type' direction='out'/>"
+    "       <arg type='au' name='device_id' direction='out'/>"
+    "     </method>"
     "     <method name='ContextCreate'>"
     "       <arg type='i' name='device_id' direction='in'/>"
     "       <arg type='i' name='priority' direction='in'/>"
@@ -51,6 +60,72 @@ static const gchar introspection_xml[] =
     " </node>";
 
 /**
+ * @brief Name of supported plugins
+ */
+static const char *supported_plugins[] = {
+    "triv2",
+};
+
+/**
+ * @brief Internal structure to hold plugin data
+ */
+typedef struct {
+  const char *name;
+  GModule *module;
+  NpumgrDevice *device;
+  uint32_t id;
+  uint32_t caps;
+  npumgr_device_type_t ptype;
+} plugin_pdata;
+
+/**
+ * @brief Registered plugins
+ */
+static GSList *registered_plugins;
+
+static void
+append_device (gpointer data, gpointer user_data) {
+  plugin_pdata *pdata = (plugin_pdata *) data;
+  npumgr_devices_id *devices = (npumgr_devices_id *) user_data;
+  NpumgrDevice *device = pdata->device;
+
+  NPUMGR_DEVICE_GET_CLASS (device)->device_get_capabilities (
+      device, &pdata->caps, &pdata->ptype);
+
+  devices->types[devices->n_devices] = pdata->ptype;
+  devices->id[devices->n_devices] = pdata->id;
+  devices->n_devices++;
+}
+
+/**
+ * @brief Insert npumgr context created
+ */
+static gboolean
+insert_context (npumgr_context handle, NpumgrContext *context) {
+  gboolean result;
+
+  G_LOCK (mutex);
+  result = g_hash_table_insert (ctx_table, GSIZE_TO_POINTER (handle), context);
+  G_UNLOCK (mutex);
+
+  return result;
+}
+
+/**
+ * @brief Remove the npumgr context
+ */
+static gboolean
+remove_context (npumgr_context handle) {
+  gboolean result;
+
+  G_LOCK (mutex);
+  result = g_hash_table_remove (ctx_table, GSIZE_TO_POINTER (handle));
+  G_UNLOCK (mutex);
+
+  return result;
+}
+
+/**
  * @brief Method callback
  */
 static void
@@ -58,31 +133,88 @@ handle_method_call (GDBusConnection *connection, const gchar *sender,
                     const gchar *object_path, const gchar *interface_name,
                     const gchar *method_name, GVariant *parameters,
                     GDBusMethodInvocation *invocation, gpointer user_data) {
-  if (g_strcmp0 (method_name, "ContextCreate") == 0) {
+  if (g_strcmp0 (method_name, "DeviceGetAvailableList") == 0) {
+    GVariantBuilder builder, builder_arr;
+    npumgr_devices_id devices = {.n_devices = 0};
+
+    g_slist_foreach (registered_plugins, append_device, &devices);
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("(uaiau)"));
+    g_variant_builder_add (&builder, "u", devices.n_devices);
+
+    g_variant_builder_init (&builder_arr, G_VARIANT_TYPE ("ai"));
+    for (int i = 0; i < devices.n_devices; i++)
+      g_variant_builder_add (&builder_arr, "i", ((int *) devices.types)[i]);
+    g_variant_builder_add_value (&builder,
+                                 g_variant_builder_end (&builder_arr));
+
+    g_variant_builder_init (&builder_arr, G_VARIANT_TYPE ("au"));
+    for (int i = 0; i < devices.n_devices; i++)
+      g_variant_builder_add (&builder_arr, "u", ((int *) devices.id)[i]);
+    g_variant_builder_add_value (&builder,
+                                 g_variant_builder_end (&builder_arr));
+
+    g_dbus_method_invocation_return_value (invocation,
+                                           g_variant_builder_end (&builder));
+  } else if (g_strcmp0 (method_name, "ContextCreate") == 0) {
     int device_id = -1;
     int priority = -1;
 
     g_variant_get (parameters, "(ii)", &device_id, &priority);
-    if (device_id >= 0 && priority >= 0) {
-      npumgrContext *ctx = new npumgrContext;
-
-      ctx->setDeviceID (device_id);
-      ctx->setPriority (priority);
-
-      g_dbus_method_invocation_return_value (
-          invocation, g_variant_new ("(t)", reinterpret_cast<uintptr_t> (ctx)));
-    } else {
+    if (device_id < 0 || priority < 0) {
       g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
                                              G_DBUS_ERROR_INVALID_ARGS,
                                              "Invalid arguments detected");
+      return;
+    }
+
+    plugin_pdata *pdata =
+        (plugin_pdata *) g_slist_nth_data (registered_plugins, device_id);
+    if (pdata == NULL) {
+      g_dbus_method_invocation_return_error (
+          invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid device id");
+      return;
+    }
+
+    NpumgrDevice *device = pdata->device;
+    npumgr_context handle;
+    npumgr_status_e status;
+
+    status = NPUMGR_DEVICE_GET_CLASS (device)->context_create (
+        device, device_id, priority, &handle);
+    if (status == NPUMGR_STATUS_SUCCESS) {
+      NpumgrContext *ctx;
+
+      ctx = g_new0 (NpumgrContext, 1);
+      ctx->handle = handle;
+      ctx->device = device;
+
+      if (insert_context (handle, ctx)) {
+        g_dbus_method_invocation_return_value (invocation,
+                                               g_variant_new ("(t)", handle));
+      } else {
+        g_dbus_method_invocation_return_error (
+            invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+            "Unable to add context: same context handle detected");
+        NPUMGR_DEVICE_GET_CLASS (device)->context_destroy (device, handle);
+        g_free (ctx);
+      }
+    } else {
+      g_dbus_method_invocation_return_error (
+          invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+          "Unable to add context: internal errors");
     }
   } else if (g_strcmp0 (method_name, "ContextDestroy") == 0) {
-    npumgrContext *ctx = NULL;
+    npumgr_context handle = 0;
 
-    g_variant_get (parameters, "(t)", reinterpret_cast<uintptr_t *> (&ctx));
-    if (ctx != NULL) {
-      delete ctx;
-      g_dbus_method_invocation_return_value (invocation, NULL);
+    g_variant_get (parameters, "(t)", &handle);
+    if (handle > 0) {
+      if (remove_context (handle))
+        g_dbus_method_invocation_return_value (invocation, NULL);
+      else
+        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                               G_DBUS_ERROR_UNKNOWN_OBJECT,
+                                               "Unable to find the context");
     } else {
       g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
                                              G_DBUS_ERROR_INVALID_ARGS,
@@ -153,6 +285,70 @@ on_name_lost (GDBusConnection *connection, const gchar *name,
 }
 
 /**
+ * @brief Destroy callback for freeing context instance
+ */
+static void
+ctx_destroy (gpointer data) {
+  NpumgrContext *ctx = static_cast<NpumgrContext *> (data);
+  NpumgrDevice *device;
+
+  g_return_if_fail (ctx != NULL);
+
+  device = ctx->device;
+  NPUMGR_DEVICE_GET_CLASS (device)->context_destroy (device, ctx->handle);
+
+  g_free (ctx);
+}
+
+static void
+plugin_pdata_free (gpointer *data) {
+  plugin_pdata *pdata = (plugin_pdata *) data;
+
+  g_module_close (pdata->module);
+  g_object_unref (pdata->device);
+  g_free (pdata);
+}
+
+/**
+ * @brief Initialize plugins
+ */
+static void
+init_plugins (void) {
+  typedef NpumgrDevice *(*NewFunc) (void);
+  GModule *module;
+  NewFunc func;
+
+  registered_plugins = NULL;
+
+  for (auto &name : supported_plugins) {
+    gchar *module_name = g_strdup_printf ("libnpumgr_%s", name);
+    module = g_module_open (module_name, G_MODULE_BIND_LAZY);
+    if (!module) {
+      g_critical ("Unable to open %s", module_name);
+      g_free (module_name);
+      continue;
+    }
+    g_free (module_name);
+
+    gchar *module_symbol = g_strdup_printf ("npumgr_device_%s_new", name);
+    if (!g_module_symbol (module, module_symbol, (gpointer *) &func)) {
+      g_critical ("Unable to find symbol %s", module_symbol);
+      g_free (module_symbol);
+      continue;
+    }
+    g_free (module_symbol);
+
+    plugin_pdata *pdata = g_new0 (plugin_pdata, 1);
+    registered_plugins = g_slist_append (registered_plugins, pdata);
+
+    pdata->name = name;
+    pdata->module = module;
+    pdata->device = func ();
+    pdata->id = g_slist_index (registered_plugins, pdata);
+  }
+}
+
+/**
  * @brief Main function for gdbus-based server
  */
 int
@@ -170,6 +366,10 @@ main (int argc, char *argv[]) {
     return -1;
   }
 
+  init_plugins ();
+
+  ctx_table =
+      g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, ctx_destroy);
   owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, "sr.odl.NPUManager.API",
                              G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
                              on_name_acquired, on_name_lost, NULL, NULL);
@@ -180,5 +380,8 @@ main (int argc, char *argv[]) {
   g_bus_unown_name (owner_id);
   g_dbus_node_info_unref (introspection_data);
 
+  g_hash_table_destroy (ctx_table);
+  g_slist_free_full (registered_plugins, (GDestroyNotify) plugin_pdata_free);
+
   return 0;
 }
index c7afe62..14acb8b 100644 (file)
@@ -31,6 +31,7 @@ static GMainLoop *_loop;
 static GMutex _mutex;
 static GCond _cond;
 static const gchar *_name_owner;
+static gboolean _running;
 
 /**
  * @brief Wait until the gdbus session is connected
@@ -55,8 +56,71 @@ wait_until_connected () {
  */
 npumgr_status_e
 npumgr_device_get_available_list (npumgr_devices_id *pdev) {
-  /* NYI */
-  return NPUMGR_STATUS_SUCCESS;
+  GDBusMessage *method_call;
+  GDBusMessage *method_reply;
+  npumgr_status_e status = NPUMGR_STATUS_SUCCESS;
+  GError *error = NULL;
+
+  g_return_val_if_fail (pdev != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (wait_until_connected (), NPUMGR_STATUS_ERR_TIMEOUT);
+
+  method_call = g_dbus_message_new_method_call (
+      _name_owner, "/sr/odl/NPUManager/APIObject", "sr.odl.NPUManager.API",
+      "DeviceGetAvailableList");
+  g_dbus_message_set_body (method_call, NULL);
+
+  method_reply = g_dbus_connection_send_message_with_reply_sync (
+      _connection, method_call, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL,
+      &error);
+
+  if (method_reply == NULL) {
+    status = NPUMGR_STATUS_ERR_FAIL;
+    goto out;
+  }
+
+  if (g_dbus_message_get_message_type (method_reply) ==
+      G_DBUS_MESSAGE_TYPE_ERROR) {
+    g_dbus_message_to_gerror (method_reply, &error);
+    g_critical ("error: %s\n", error->message);
+    g_error_free (error);
+    status = NPUMGR_STATUS_ERR_FAIL;
+    goto out;
+  }
+
+  {
+    GVariantIter *iter_type;
+    GVariantIter *iter_id;
+    uint32_t idx;
+
+    g_variant_get (g_dbus_message_get_body (method_reply), "(uaiau)",
+                   &pdev->n_devices, &iter_type, &iter_id);
+
+    if (pdev->n_devices > NPUMGR_MAX_DEVICES) {
+      g_critical ("Too many devices: %u", pdev->n_devices);
+      status = NPUMGR_STATUS_ERR_FAIL;
+      goto out_iter;
+    }
+
+    idx = 0;
+    while (idx < NPUMGR_MAX_DEVICES &&
+           g_variant_iter_loop (iter_type, "i", &pdev->types[idx++]))
+      ;
+
+    idx = 0;
+    while (idx < NPUMGR_MAX_DEVICES &&
+           g_variant_iter_loop (iter_id, "u", &pdev->id[idx++]))
+      ;
+
+  out_iter:
+    g_variant_iter_free (iter_type);
+    g_variant_iter_free (iter_id);
+  }
+
+out:
+  g_object_unref (method_call);
+  g_object_unref (method_reply);
+
+  return status;
 }
 
 /**
@@ -153,9 +217,9 @@ npumgr_status_e
 npumgr_network_create (npumgr_context ctx, int num_files,
                        npumgr_network_defn *input_files,
                        npumgr_buffer_t in_buffer_type, int in_tensor_cnt,
-                       const char *const *input_tensor_names,
+                       const char *const *in_tensor_names,
                        npumgr_buffer_t out_buffer_type, int out_tensor_cnt,
-                       const char *const *output_tensor_names,
+                       const char *const *out_tensor_names,
                        npumgr_network *out_nw_handle) {
   /* NYI */
   return NPUMGR_STATUS_SUCCESS;
@@ -345,6 +409,12 @@ npumgr_thread (gpointer data) {
                         on_name_vanished, NULL, NULL);
 
   _loop = g_main_loop_new (NULL, FALSE);
+
+  g_mutex_lock (&_mutex);
+  _running = TRUE;
+  g_cond_broadcast (&_cond);
+  g_mutex_unlock (&_mutex);
+
   g_main_loop_run (_loop);
 
   g_bus_unwatch_name (watcher_id);
@@ -359,6 +429,7 @@ void
 init_npumgr_api (void) {
   _connection = NULL;
   _loop = NULL;
+  _running = FALSE;
   _thread = g_thread_new (NULL, npumgr_thread, NULL);
 }
 
@@ -368,7 +439,14 @@ init_npumgr_api (void) {
 void
 fini_npumgr_api (void) {
   if (_thread) {
+    gint64 end_time = g_get_monotonic_time () + 5 * G_TIME_SPAN_SECOND;
+
+    g_mutex_lock (&_mutex);
+    if (_running == FALSE)
+      g_cond_wait_until (&_cond, &_mutex, end_time);
     g_main_loop_quit (_loop);
+    g_mutex_unlock (&_mutex);
+
     g_thread_join (_thread);
   }
 }
index 89f66a7..c17dcf8 100644 (file)
@@ -234,6 +234,7 @@ typedef enum _npumgr_network_file_type {
        NPUMGR_NETWORK_FILE_CAFFE_MODEL_FILE,   /**< Network file of type caffe. */
        NPUMGR_NETWORK_FILE_CAFFE_WEIGHTS_FILE, /**< Network file of type caffe weights. */
 
+       NPUMGR_NETWORK_FILE_TVN,        /**< Trinity Vision NPU */
 
        NPUMGR_NETWORK_TYPE_MAX
 }npumgr_network_file_t;
@@ -266,7 +267,7 @@ typedef enum _npumgr_buffer_type {
        NPUMGR_BUF_TYPE_DMABUF, /**< This type of buffer os imported by dmabuf fd or importable memory. */
        NPUMGR_BUF_TYPE_CUSTOM_INDEX, /**< DPB Buffer (custom) index for memory. */
        NPUMGR_BUF_TYPE_PATCH, /**< This type of buffer is not allocated but it is patched by npumgr with help from other drivers. */
-       NPUMGR_BUT_TYPE_MAX
+       NPUMGR_BUF_TYPE_MAX
 }npumgr_buffer_t;
 
 /**
diff --git a/tests/apptests/npumgr/npumgr_device.cc b/tests/apptests/npumgr/npumgr_device.cc
new file mode 100644 (file)
index 0000000..27a5def
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * Proprietary
+ * Copyright (C) 2021 Samsung Electronics
+ * Copyright (C) 2021 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file npumgr_device.cc
+ * @date 03 May 2021
+ * @brief a parent class (dummy) of VD NPU Manager device.
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include "npumgr_device.h"
+
+G_DEFINE_TYPE (NpumgrDevice, npumgr_device, G_TYPE_OBJECT);
+
+static void
+npumgr_device_class_init (NpumgrDeviceClass *klass) {
+  /* NYI */
+}
+
+static void
+npumgr_device_init (NpumgrDevice *self) {
+  /* NYI */
+}
index 8b0fc2f..c0e9810 100644 (file)
  */
 
 #include <iostream>
+
+#include <fcntl.h>
+#include <unistd.h>
+
 #include "npumgr_api.h"
 
 int
 main (int argc, char **argv) {
   npumgr_context ctx;
+  npumgr_devices_id list;
   npumgr_status_e status;
+  int ret = -EINVAL;
+
+  status = npumgr_device_get_available_list (&list);
+  if (status != NPUMGR_STATUS_SUCCESS) {
+    std::cerr << "Unable to get available device list, " << status << "\n";
+    return ret;
+  }
+
+  /* TODO: find the first NPU device */
+  bool found = false;
+  uint32_t id;
+
+  for (int i = 0; i < list.n_devices; i++) {
+    if (list.types[i] == NPUMGR_DEVICE_TYPE_NPU) {
+      found = true;
+      id = list.id[i];
+      break;
+    }
+  }
+
+  if (!found) {
+    std::cerr << "No available device\n";
+    return ret;
+  }
 
-  status = npumgr_context_create (0 /* device_id */, 0 /* priority */, &ctx);
+  status = npumgr_context_create (id, NPUMGR_FLAG_PRIORITY_DEFAULT, &ctx);
   if (status != NPUMGR_STATUS_SUCCESS) {
     std::cerr << "Unable to create a npumgr context, " << status << "\n";
-    return status;
+    return ret;
   }
 
   status = npumgr_context_destroy (ctx);
   if (status != NPUMGR_STATUS_SUCCESS) {
     std::cerr << "Unable to destroy the npumgr context, " << status << "\n";
-    return status;
+    return ret;
   }
 
-  return 0;
+  return ret;
 }
diff --git a/tests/apptests/npumgr/npumgr_triv2.cc b/tests/apptests/npumgr/npumgr_triv2.cc
new file mode 100644 (file)
index 0000000..f06da6d
--- /dev/null
@@ -0,0 +1,219 @@
+/**
+ * Proprietary
+ * Copyright (C) 2021 Samsung Electronics
+ * Copyright (C) 2021 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file npumgr_triv2.cc
+ * @date 03 May 2021
+ * @brief TRIV2 device plugin of VD NPU Manager
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include "npumgr_triv2.h"
+
+/* npu-engine API */
+#include <libnpuhost.h>
+
+#include <vector>
+#include <fcntl.h>
+#include <unistd.h>
+
+extern "C" {
+NpumgrDevice *npumgr_device_triv2_new (void);
+}
+
+/**
+ * @brief Class for triv2 npumgr context
+ */
+class NpumgrContextTriv2 {
+ public:
+  NpumgrContextTriv2 (npudev_h dev, int priority)
+      : dev_ (dev), priority_ (priority) {
+    handle_ = g_atomic_int_add (&g_ctx_handle, 1);
+  }
+
+  ~NpumgrContextTriv2 () {}
+
+  npumgr_context getHandle () { return handle_; }
+
+ private:
+  static volatile guint g_ctx_handle;
+
+  npumgr_context handle_;
+  npudev_h dev_;
+  int priority_;
+};
+
+static void
+ctx_destroy (gpointer data) {
+  NpumgrContextTriv2 *ctx = static_cast<NpumgrContextTriv2 *> (data);
+
+  delete ctx;
+}
+
+volatile guint NpumgrContextTriv2::g_ctx_handle = 1;
+
+/**
+ * @brief Private members in NpumgrDeviceTriv2
+ */
+struct _NpumgrDeviceTriv2Private {
+  npudev_h dev;
+  GMutex mutex;
+  GHashTable *ctx_table;
+};
+
+#define NPUMGR_DEVICE(obj) &((obj)->parent)
+#define NPUMGR_DEVICE_TRIV2_GET_PRIVATE(obj)                  \
+  ((NpumgrDeviceTriv2Private *) g_type_instance_get_private ( \
+      (GTypeInstance *) obj, NPUMGR_TYPE_DEVICE_TRIV2))
+
+G_DEFINE_TYPE_WITH_PRIVATE (NpumgrDeviceTriv2, npumgr_device_triv2,
+                            NPUMGR_TYPE_DEVICE);
+
+/* GObject */
+static void triv2_finalize (GObject *object);
+/* NpumgrDevice */
+static npumgr_status_e triv2_device_get_capabilities (
+    NpumgrDevice *device, uint32_t *caps, npumgr_device_type_t *ptype);
+static npumgr_status_e triv2_context_create (NpumgrDevice *device,
+                                             int device_id, int priority,
+                                             npumgr_context *out_ctx);
+static npumgr_status_e triv2_context_destroy (NpumgrDevice *device,
+                                              npumgr_context ctx);
+
+extern NpumgrDevice *
+npumgr_device_triv2_new (void) {
+  gpointer instance = g_object_new (NPUMGR_TYPE_DEVICE_TRIV2, NULL);
+
+  return NPUMGR_DEVICE ((NpumgrDeviceTriv2 *) instance);
+}
+
+static void
+npumgr_device_triv2_class_init (NpumgrDeviceTriv2Class *klass) {
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  NpumgrDeviceClass *npumgr_device_class = &(klass->parent_class);
+
+  /* GObject */
+  gobject_class->finalize = triv2_finalize;
+  /* NpumgrDevice */
+  npumgr_device_class->device_get_capabilities = triv2_device_get_capabilities;
+  npumgr_device_class->context_create = triv2_context_create;
+  npumgr_device_class->context_destroy = triv2_context_destroy;
+  /* NYI */
+}
+
+static void
+npumgr_device_triv2_init (NpumgrDeviceTriv2 *self) {
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+  int status;
+
+  memset (priv, '\x00', sizeof (*priv));
+  g_mutex_init (&priv->mutex);
+  priv->ctx_table =
+      g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, ctx_destroy);
+
+  /** TODO: How npumgr supports multiple TRIV2 devices? */
+  status = getNPUdeviceByTypeAny (&priv->dev, NPUCOND_TRIV2_CONN_SOCIP, 2);
+  if (status != 0) {
+    g_critical ("Unable to find/open 2-TOPS TRIV2 device\n");
+    priv->dev = NULL;
+    return;
+  }
+  /* NYI */
+}
+
+static void
+triv2_finalize (GObject *object) {
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (object);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  g_mutex_clear (&priv->mutex);
+  g_hash_table_destroy (priv->ctx_table);
+
+  if (priv->dev)
+    putNPUdevice (priv->dev);
+}
+
+/* Filling the virtual function table of TRIV2 */
+static npumgr_status_e
+triv2_device_get_capabilities (NpumgrDevice *device, uint32_t *caps,
+                               npumgr_device_type_t *ptype) {
+  g_return_val_if_fail (device != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (caps != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (ptype != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (device);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->dev != NULL,
+                        NPUMGR_STATUS_ERR_DEVICE_UNAVAILABLE);
+
+  *caps = NPUMGR_DEVICE_CAPABILITY_MMAP |
+          NPUMGR_DEVICE_CAPABILITY_MULTICONTEXT | NPUMGR_DEVICE_CAPABILITY_MMU |
+          NPUMGR_DEVICE_CAPABILITY_REALTIME;
+  *ptype = NPUMGR_DEVICE_TYPE_NPU;
+
+  return NPUMGR_STATUS_SUCCESS;
+}
+
+static gboolean
+insert_context (NpumgrDeviceTriv2Private *priv, npumgr_context handle,
+                NpumgrContextTriv2 *context) {
+  gboolean result;
+
+  g_mutex_lock (&priv->mutex);
+  result =
+      g_hash_table_insert (priv->ctx_table, GSIZE_TO_POINTER (handle), context);
+  g_mutex_unlock (&priv->mutex);
+
+  return result;
+}
+
+static gboolean
+remove_context (NpumgrDeviceTriv2Private *priv, npumgr_context handle) {
+  gboolean result;
+
+  g_mutex_lock (&priv->mutex);
+  result = g_hash_table_remove (priv->ctx_table, GSIZE_TO_POINTER (handle));
+  g_mutex_unlock (&priv->mutex);
+
+  return result;
+}
+
+static npumgr_status_e
+triv2_context_create (NpumgrDevice *device, int device_id, int priority,
+                      npumgr_context *ctx_handle) {
+  g_return_val_if_fail (device != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (ctx_handle != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (device);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->dev != NULL,
+                        NPUMGR_STATUS_ERR_DEVICE_UNAVAILABLE);
+
+  NpumgrContextTriv2 *context = new NpumgrContextTriv2 (priv->dev, priority);
+  *ctx_handle = context->getHandle ();
+
+  if (insert_context (priv, context->getHandle (), context)) {
+    return NPUMGR_STATUS_SUCCESS;
+  } else {
+    return NPUMGR_STATUS_ERR_FAIL;
+  }
+}
+
+static npumgr_status_e
+triv2_context_destroy (NpumgrDevice *device, npumgr_context ctx_handle) {
+  g_return_val_if_fail (device != NULL, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (ctx_handle != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (device);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  if (remove_context (priv, ctx_handle))
+    return NPUMGR_STATUS_SUCCESS;
+  else
+    return NPUMGR_STATUS_ERR_CTX_INVALID;
+}
diff --git a/tests/apptests/npumgr/npumgr_triv2.h b/tests/apptests/npumgr/npumgr_triv2.h
new file mode 100644 (file)
index 0000000..c8a3467
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * Proprietary
+ * Copyright (C) 2021 Samsung Electronics
+ * Copyright (C) 2021 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file npumgr_triv2.h
+ * @date 03 May 2021
+ * @brief Internal header of TRIV2 device plugin
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#ifndef __NPUMGR_TRIV2_H__
+#define __NPUMGR_TRIV2_H__
+
+/* npumgr device API */
+#include <npumgr_device.h>
+
+G_BEGIN_DECLS
+
+#define NPUMGR_TYPE_DEVICE_TRIV2 (npumgr_device_triv2_get_type ())
+#define NPUMGR_DEVICE_TRIV2(obj)                                \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), NPUMGR_TYPE_DEVICE_TRIV2, \
+                               NpumgrDeviceTriv2))
+
+typedef struct _NpumgrDeviceTriv2 NpumgrDeviceTriv2;
+typedef struct _NpumgrDeviceTriv2Class NpumgrDeviceTriv2Class;
+typedef struct _NpumgrDeviceTriv2Private NpumgrDeviceTriv2Private;
+
+/**
+ * @brief npumgr device class for TRIV2.
+ *
+ * NpumgrDeviceTriv2 inherits NpumgrDevice.
+ */
+struct _NpumgrDeviceTriv2 {
+  NpumgrDevice parent; /**< parent class object */
+};
+
+/**
+ * @brief NpumgrDeviceTriv2Class data structure.
+ *
+ * NpumgrDeviceTriv2Class inherits NpumgrDeviceClass.
+ */
+struct _NpumgrDeviceTriv2Class {
+  NpumgrDeviceClass parent_class; /**< inherits class object */
+};
+
+G_END_DECLS
+
+#endif