[VD/NPUMGR] Implement network/tensor query functions
authorDongju Chae <dongju.chae@samsung.com>
Tue, 18 May 2021 10:07:35 +0000 (19:07 +0900)
committer문지중/On-Device Lab(SR)/Principal Engineer/삼성전자 <jijoong.moon@samsung.com>
Tue, 25 May 2021 03:37:48 +0000 (12:37 +0900)
This patch implements network/tensor query functions of
TRIV2's VD NPU Manager plugin.

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_test.cc
tests/apptests/npumgr/npumgr_triv2.cc

index 6c1a38c..f7c9252 100644 (file)
@@ -31,6 +31,7 @@ if glib_dep.found() and giounix_dep.found()
 
   executable ('apptest_npumgr',
     'npumgr_test.cc',
+    dependencies : glib_dep,
     link_with : npumgr_lib,
     install : true,
     install_rpath : ne_libdir,
index 42392ff..698d2fc 100644 (file)
@@ -13,7 +13,6 @@
 
 #include <stdlib.h>
 #include <unistd.h>
-#include <cstdint>
 #include <vector>
 #include <string>
 
@@ -72,6 +71,24 @@ static const gchar introspection_xml[] =
     "       <arg type='t' name='ctx_handle' direction='in'/>"
     "       <arg type='t' name='nw_handle' direction='in'/>"
     "     </method>"
+    "     <method name='QueryNetwork'>"
+    "       <arg type='t' name='ctx_handle' direction='in'/>"
+    "       <arg type='t' name='nw_handle' direction='in'/>"
+    "       <arg type='u' name='n_input' direction='out'/>"
+    "       <arg type='u' name='n_output' direction='out'/>"
+    "     </method>"
+    "     <method name='QueryInput'>"
+    "       <arg type='t' name='ctx_handle' direction='in'/>"
+    "       <arg type='t' name='nw_handle' direction='in'/>"
+    "       <arg type='i' name='index' direction='in'/>"
+    "       <arg type='v' name='data' direction='out'/>"
+    "     </method>"
+    "     <method name='QueryOutput'>"
+    "       <arg type='t' name='ctx_handle' direction='in'/>"
+    "       <arg type='t' name='nw_handle' direction='in'/>"
+    "       <arg type='i' name='index' direction='in'/>"
+    "       <arg type='v' name='data' direction='out'/>"
+    "     </method>"
     "   </interface>"
     " </node>";
 
@@ -346,6 +363,115 @@ handle_method_call (GDBusConnection *connection, const gchar *sender,
                                              G_DBUS_ERROR_INVALID_ARGS,
                                              "Invalid arguments detected");
     }
+  } else if (g_strcmp0 (method_name, "QueryNetwork") == 0) {
+    npumgr_context ctx_handle = 0;
+    npumgr_network nw_handle = 0;
+
+    g_variant_get (parameters, "(tt)", (guint64 *) &ctx_handle,
+                   (guint64 *) &nw_handle);
+    if (ctx_handle > 0 && nw_handle > 0) {
+      NpumgrContext *context = find_context (ctx_handle);
+      if (context != NULL) {
+        NpumgrDevice *device = context->device;
+        npumgr_status_e status;
+
+        npumgr_query_inout_num data;
+        status = NPUMGR_DEVICE_GET_CLASS (device)->query_network (
+            device, ctx_handle, nw_handle, &data);
+        if (status == NPUMGR_STATUS_SUCCESS) {
+          g_dbus_method_invocation_return_value (
+              invocation, g_variant_new ("(uu)", data.n_input, data.n_output));
+        } else {
+          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_UNKNOWN_OBJECT,
+                                                 "Unable to find the network");
+        }
+      } 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,
+                                             "Invalid arguments detected");
+    }
+  } else if (g_strcmp0 (method_name, "QueryInput") == 0) {
+    npumgr_context ctx_handle = 0;
+    npumgr_network nw_handle = 0;
+    int index = -1;
+
+    g_variant_get (parameters, "(tti)", (guint64 *) &ctx_handle,
+                   (guint64 *) &nw_handle, &index);
+    if (ctx_handle > 0 && nw_handle > 0 && index >= 0) {
+      NpumgrContext *context = find_context (ctx_handle);
+      if (context != NULL) {
+        NpumgrDevice *device = context->device;
+        npumgr_status_e status;
+
+        npumgr_query_tensor_attr data;
+        status = NPUMGR_DEVICE_GET_CLASS (device)->query_input (
+            device, ctx_handle, nw_handle, index, &data);
+        if (status == NPUMGR_STATUS_SUCCESS) {
+          GBytes *bytes = g_bytes_new (&data, sizeof (data));
+          GVariant *variant =
+              g_variant_new_from_bytes (G_VARIANT_TYPE ("ay"), bytes, TRUE);
+          g_dbus_method_invocation_return_value (
+              invocation, g_variant_new ("(v)", variant));
+          g_bytes_unref (bytes);
+        } else {
+          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_FAILED,
+                                                 "Unable to find input attr");
+        }
+      } 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,
+                                             "Invalid arguments detected");
+    }
+  } else if (g_strcmp0 (method_name, "QueryOutput") == 0) {
+    npumgr_context ctx_handle = 0;
+    npumgr_network nw_handle = 0;
+    int index = -1;
+
+    g_variant_get (parameters, "(tti)", (guint64 *) &ctx_handle,
+                   (guint64 *) &nw_handle, &index);
+    if (ctx_handle > 0 && nw_handle > 0 && index >= 0) {
+      NpumgrContext *context = find_context (ctx_handle);
+      if (context != NULL) {
+        NpumgrDevice *device = context->device;
+        npumgr_status_e status;
+
+        npumgr_query_tensor_attr data;
+        status = NPUMGR_DEVICE_GET_CLASS (device)->query_output (
+            device, ctx_handle, nw_handle, index, &data);
+        if (status == NPUMGR_STATUS_SUCCESS) {
+          GBytes *bytes = g_bytes_new (&data, sizeof (data));
+          GVariant *variant =
+              g_variant_new_from_bytes (G_VARIANT_TYPE ("ay"), bytes, TRUE);
+          g_dbus_method_invocation_return_value (
+              invocation, g_variant_new ("(v)", variant));
+          g_bytes_unref (bytes);
+        } else {
+          g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                                                 G_DBUS_ERROR_FAILED,
+                                                 "Unable to find output attr");
+        }
+      } 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,
+                                             "Invalid arguments detected");
+    }
   }
 }
 
index 79830da..44e891b 100644 (file)
@@ -416,8 +416,44 @@ npumgr_buffer_unmap (npumgr_context ctx, npumgr_buffer *buf) {
 npumgr_status_e
 npumgr_query_network (npumgr_context ctx, npumgr_network nw_handle,
                       npumgr_query_inout_num *data) {
-  /* 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 (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",
+      "QueryNetwork");
+  g_dbus_message_set_body (method_call, g_variant_new ("(tt)", ctx, nw_handle));
+
+  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;
+  }
+
+  g_variant_get (g_dbus_message_get_body (method_reply), "(uu)", &data->n_input,
+                 &data->n_output);
+
+out:
+  g_object_unref (method_call);
+  g_object_unref (method_reply);
+
+  return status;
 }
 
 /**
@@ -426,8 +462,57 @@ npumgr_query_network (npumgr_context ctx, npumgr_network nw_handle,
 npumgr_status_e
 npumgr_query_input (npumgr_context ctx, npumgr_network nw_handle, int index,
                     npumgr_query_tensor_attr *data) {
-  /* NYI */
-  return NPUMGR_STATUS_SUCCESS;
+  GDBusMessage *method_call;
+  GDBusMessage *method_reply;
+  npumgr_status_e status = NPUMGR_STATUS_SUCCESS;
+  GError *error = NULL;
+  GVariant *variant;
+  GBytes *bytes;
+
+  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",
+      "QueryInput");
+  g_dbus_message_set_body (method_call,
+                           g_variant_new ("(tti)", ctx, nw_handle, index));
+
+  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;
+  }
+
+  g_variant_get (g_dbus_message_get_body (method_reply), "(v)", &variant);
+  bytes = g_variant_get_data_as_bytes (variant);
+
+  if (g_bytes_get_size (bytes) != sizeof (*data)) {
+    status = NPUMGR_STATUS_ERR_FAIL;
+    goto out_unref;
+  }
+
+  memcpy (data, g_bytes_get_data (bytes, NULL), sizeof (*data));
+
+out_unref:
+  g_bytes_unref (bytes);
+
+out:
+  g_object_unref (method_call);
+  g_object_unref (method_reply);
+
+  return status;
 }
 
 /**
@@ -436,8 +521,57 @@ npumgr_query_input (npumgr_context ctx, npumgr_network nw_handle, int index,
 npumgr_status_e
 npumgr_query_output (npumgr_context ctx, npumgr_network nw_handle, int index,
                      npumgr_query_tensor_attr *data) {
-  /* NYI */
-  return NPUMGR_STATUS_SUCCESS;
+  GDBusMessage *method_call;
+  GDBusMessage *method_reply;
+  npumgr_status_e status = NPUMGR_STATUS_SUCCESS;
+  GError *error = NULL;
+  GVariant *variant;
+  GBytes *bytes;
+
+  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",
+      "QueryOutput");
+  g_dbus_message_set_body (method_call,
+                           g_variant_new ("(tti)", ctx, nw_handle, index));
+
+  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;
+  }
+
+  g_variant_get (g_dbus_message_get_body (method_reply), "(v)", &variant);
+  bytes = g_variant_get_data_as_bytes (variant);
+
+  if (g_bytes_get_size (bytes) != sizeof (*data)) {
+    status = NPUMGR_STATUS_ERR_FAIL;
+    goto out_unref;
+  }
+
+  memcpy (data, g_bytes_get_data (bytes, NULL), sizeof (*data));
+
+out_unref:
+  g_bytes_unref (bytes);
+
+out:
+  g_object_unref (method_call);
+  g_object_unref (method_reply);
+
+  return status;
 }
 
 /**
index fd00e22..30b59aa 100644 (file)
 #include <fcntl.h>
 #include <unistd.h>
 
+#include <glib.h>
+
 #include "npumgr_api.h"
 
 int
 main (int argc, char **argv) {
-  npumgr_context ctx;
-  npumgr_network nw_handle;
+  npumgr_context context;
+  npumgr_network network;
   npumgr_devices_id list;
   npumgr_status_e status;
   int fd, ret = -EINVAL;
 
   if (argc != 2) {
-    std::cerr << "Please provide network file name\n";
+    std::cerr << "Please provide network file name (.tvn)\n";
+    return ret;
+  }
+
+  if (!g_str_has_suffix (argv[1], ".tvn")) {
+    std::cerr << "Invalid model path: " << argv[1] << "\n";
     return ret;
   }
 
@@ -54,7 +61,7 @@ main (int argc, char **argv) {
     return ret;
   }
 
-  status = npumgr_context_create (id, NPUMGR_FLAG_PRIORITY_DEFAULT, &ctx);
+  status = npumgr_context_create (id, NPUMGR_FLAG_PRIORITY_DEFAULT, &context);
   if (status != NPUMGR_STATUS_SUCCESS) {
     std::cerr << "Unable to create a npumgr context, " << status << "\n";
     return ret;
@@ -65,16 +72,53 @@ main (int argc, char **argv) {
     npumgr_network_defn input_files[] = {{NPUMGR_NETWORK_FILE_TVN, fd}};
     const char *input_tensor_names[] = {"input"};
     const char *output_tensor_names[] = {"output"};
+    npumgr_query_inout_num inout_num;
+    npumgr_query_tensor_attr *input_attr;
+    npumgr_query_tensor_attr *output_attr;
 
     status = npumgr_network_create (
-        ctx, 1, input_files, NPUMGR_BUF_TYPE_DRIVER, 1, input_tensor_names,
-        NPUMGR_BUF_TYPE_DRIVER, 1, output_tensor_names, &nw_handle);
+        context, 1, input_files, NPUMGR_BUF_TYPE_DRIVER, 1, input_tensor_names,
+        NPUMGR_BUF_TYPE_DRIVER, 1, output_tensor_names, &network);
     if (status != NPUMGR_STATUS_SUCCESS) {
       std::cerr << "Unable to create a npumgr network, " << status << "\n";
       goto out;
     }
 
-    status = npumgr_network_destroy (ctx, nw_handle);
+    status = npumgr_query_network (context, network, &inout_num);
+    if (status != NPUMGR_STATUS_SUCCESS) {
+      std::cerr << "Unable to query network info, " << status << "\n";
+      goto out;
+    }
+
+    input_attr = g_new0 (npumgr_query_tensor_attr, inout_num.n_input);
+    output_attr = g_new0 (npumgr_query_tensor_attr, inout_num.n_input);
+
+    for (uint32_t i = 0; i < inout_num.n_input; i++) {
+      status = npumgr_query_input (context, network, i, &input_attr[i]);
+      if (status != NPUMGR_STATUS_SUCCESS) {
+        std::cerr << "Unable to query input info, " << status << "\n";
+        g_free (input_attr);
+        g_free (output_attr);
+        goto out;
+      }
+    }
+
+    for (uint32_t i = 0; i < inout_num.n_output; i++) {
+      status = npumgr_query_output (context, network, i, &output_attr[i]);
+      if (status != NPUMGR_STATUS_SUCCESS) {
+        std::cerr << "Unable to query output info, " << status << "\n";
+        g_free (input_attr);
+        g_free (output_attr);
+        goto out;
+      }
+    }
+
+    /* NYI: DO SOMETHING */
+
+    g_free (input_attr);
+    g_free (output_attr);
+
+    status = npumgr_network_destroy (context, network);
     if (status != NPUMGR_STATUS_SUCCESS) {
       std::cerr << "Unable to destroy the npumgr network, " << status << "\n";
       goto out;
@@ -89,7 +133,7 @@ out:
   if (fd >= 0)
     close (fd);
 
-  status = npumgr_context_destroy (ctx);
+  status = npumgr_context_destroy (context);
   if (status != NPUMGR_STATUS_SUCCESS) {
     std::cerr << "Unable to destroy the npumgr context, " << status << "\n";
     return ret;
index b461c4c..62a18bd 100644 (file)
@@ -16,9 +16,9 @@
 /* npu-engine API */
 #include <libnpuhost.h>
 
-#include <vector>
 #include <fcntl.h>
 #include <unistd.h>
+#include <vector>
 
 extern "C" {
 NpumgrDevice *npumgr_device_triv2_new (void);
@@ -141,6 +141,10 @@ class NpumgrContextTriv2 {
   gboolean removeNetwork (npumgr_network handle) {
     return g_hash_table_remove (nw_table_, GSIZE_TO_POINTER (handle));
   }
+  NpumgrNetworkTriv2 *findNetwork (npumgr_network handle) {
+    return (NpumgrNetworkTriv2 *) g_hash_table_lookup (
+        nw_table_, GSIZE_TO_POINTER (handle));
+  }
 
   npumgr_context getHandle () { return handle_; }
 
@@ -185,7 +189,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (NpumgrDeviceTriv2, npumgr_device_triv2,
 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);
+    NpumgrDevice *device, guint *caps, npumgr_device_type_t *ptype);
 static npumgr_status_e triv2_context_create (NpumgrDevice *device,
                                              int device_id, int priority,
                                              npumgr_context *out_ctx);
@@ -200,6 +204,18 @@ static npumgr_status_e triv2_network_create (
 static npumgr_status_e triv2_network_destroy (NpumgrDevice *device,
                                               npumgr_context ctx,
                                               npumgr_network nw_handle);
+static npumgr_status_e triv2_query_network (NpumgrDevice *device,
+                                            npumgr_context ctx,
+                                            npumgr_network nw_handle,
+                                            npumgr_query_inout_num *data);
+static npumgr_status_e triv2_query_input (NpumgrDevice *device,
+                                          npumgr_context ctx,
+                                          npumgr_network nw_handle, int index,
+                                          npumgr_query_tensor_attr *data);
+static npumgr_status_e triv2_query_output (NpumgrDevice *device,
+                                           npumgr_context ctx,
+                                           npumgr_network nw_handle, int index,
+                                           npumgr_query_tensor_attr *data);
 
 extern NpumgrDevice *
 npumgr_device_triv2_new (void) {
@@ -221,6 +237,9 @@ npumgr_device_triv2_class_init (NpumgrDeviceTriv2Class *klass) {
   npumgr_device_class->context_destroy = triv2_context_destroy;
   npumgr_device_class->network_create = triv2_network_create;
   npumgr_device_class->network_destroy = triv2_network_destroy;
+  npumgr_device_class->query_network = triv2_query_network;
+  npumgr_device_class->query_input = triv2_query_input;
+  npumgr_device_class->query_output = triv2_query_output;
   /* NYI */
 }
 
@@ -258,7 +277,7 @@ triv2_finalize (GObject *object) {
 
 /* Filling the virtual function table of TRIV2 */
 static npumgr_status_e
-triv2_device_get_capabilities (NpumgrDevice *device, uint32_t *caps,
+triv2_device_get_capabilities (NpumgrDevice *device, guint *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);
@@ -417,3 +436,107 @@ triv2_network_destroy (NpumgrDevice *device, npumgr_context ctx_handle,
     return NPUMGR_STATUS_ERR_FAIL;
   }
 }
+
+static npumgr_status_e
+triv2_query_network (NpumgrDevice *device, npumgr_context ctx_handle,
+                     npumgr_network nw_handle,
+                     npumgr_query_inout_num *inout_num) {
+  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);
+  g_return_val_if_fail (nw_handle != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (inout_num != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (device);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  NpumgrContextTriv2 *context = find_context (priv, ctx_handle);
+  g_return_val_if_fail (context != NULL, NPUMGR_STATUS_ERR_CTX_INVALID);
+
+  NpumgrNetworkTriv2 *network = context->findNetwork (nw_handle);
+  g_return_val_if_fail (network != NULL, NPUMGR_STATUS_ERR_MODEL_INVALID);
+
+  inout_num->n_input = network->getInTensorCnt ();
+  inout_num->n_output = network->getOutTensorCnt ();
+
+  return NPUMGR_STATUS_SUCCESS;
+}
+
+static npumgr_status_e
+triv2_query_input (NpumgrDevice *device, npumgr_context ctx_handle,
+                   npumgr_network nw_handle, int index,
+                   npumgr_query_tensor_attr *attr) {
+  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);
+  g_return_val_if_fail (nw_handle != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (index >= 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (attr != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (device);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  NpumgrContextTriv2 *context = find_context (priv, ctx_handle);
+  g_return_val_if_fail (context != NULL, NPUMGR_STATUS_ERR_CTX_INVALID);
+
+  NpumgrNetworkTriv2 *network = context->findNetwork (nw_handle);
+  g_return_val_if_fail (network != NULL, NPUMGR_STATUS_ERR_MODEL_INVALID);
+
+  attr->n_dims = MAX_RANK;
+  for (guint rank = 0; rank < MAX_RANK; rank++)
+    attr->dims[rank] = network->getInTensorDim (index, rank);
+  g_snprintf (attr->name, NPUMGR_MAX_NAME_LEN, "%s",
+              network->getInTensorName (index));
+
+  /* FIXME: their exact meaning and usage? */
+  for (guint rank = 0; rank < MAX_RANK; rank++) attr->strides[rank] = 1;
+  attr->plane = 3;
+
+  attr->size = network->getInTensorSize (index);
+  attr->fmt = NPUMGR_TENSOR_FMT_NHWC;
+  attr->type = NPUMGR_TENSOR_DATA_UINT8;
+
+  attr->quant_type = NPUMGR_TENSOR_QNT_AFFINE_ASYMM;
+  attr->quant_data.affine.scale = network->getInTensorQuantScale (index);
+  attr->quant_data.affine.zeroPoint = network->getInTensorQuantZero (index);
+
+  return NPUMGR_STATUS_SUCCESS;
+}
+
+static npumgr_status_e
+triv2_query_output (NpumgrDevice *device, npumgr_context ctx_handle,
+                    npumgr_network nw_handle, int index,
+                    npumgr_query_tensor_attr *attr) {
+  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);
+  g_return_val_if_fail (nw_handle != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (index >= 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+  g_return_val_if_fail (attr != 0, NPUMGR_STATUS_ERR_PARAM_INVALID);
+
+  NpumgrDeviceTriv2 *self = NPUMGR_DEVICE_TRIV2 (device);
+  NpumgrDeviceTriv2Private *priv = NPUMGR_DEVICE_TRIV2_GET_PRIVATE (self);
+
+  NpumgrContextTriv2 *context = find_context (priv, ctx_handle);
+  g_return_val_if_fail (context != NULL, NPUMGR_STATUS_ERR_CTX_INVALID);
+
+  NpumgrNetworkTriv2 *network = context->findNetwork (nw_handle);
+  g_return_val_if_fail (network != NULL, NPUMGR_STATUS_ERR_MODEL_INVALID);
+
+  attr->n_dims = MAX_RANK;
+  for (guint rank = 0; rank < MAX_RANK; rank++)
+    attr->dims[rank] = network->getOutTensorDim (index, rank);
+  g_snprintf (attr->name, NPUMGR_MAX_NAME_LEN, "%s",
+              network->getOutTensorName (index));
+
+  /* FIXME: their exact meaning and usage? */
+  for (guint rank = 0; rank < MAX_RANK; rank++) attr->strides[rank] = 1;
+  attr->plane = 3;
+
+  attr->size = network->getOutTensorSize (index);
+  attr->fmt = NPUMGR_TENSOR_FMT_NHWC;
+  attr->type = NPUMGR_TENSOR_DATA_UINT8;
+
+  attr->quant_type = NPUMGR_TENSOR_QNT_AFFINE_ASYMM;
+  attr->quant_data.affine.scale = network->getOutTensorQuantScale (index);
+  attr->quant_data.affine.zeroPoint = network->getOutTensorQuantZero (index);
+
+  return NPUMGR_STATUS_SUCCESS;
+}