[Host/Refactor] Move libnpuhost codes to ./src/host/
[platform/adaptation/npu/trix-engine.git] / src / core / ne-handler.cc
1 /**
2  * Proprietary
3  * Copyright (C) 2020 Samsung Electronics
4  * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
5  */
6 /**
7  * @file ne-handler.cc
8  * @date 03 Apr 2020
9  * @brief Impelemetation of NPU Engine entrypoint that handles APIs from host
10  * @see https://code.sec.samsung.net/confluence/display/ODLC/2020+Overall+Software+Stack
11  * @author Dongju Chae <dongju.chae@samsung.com>
12  * @bug No known bugs except for NYI items
13  */
14
15 #include "ne-handler.h"
16 #include "ne-data.h"
17
18 #include <npubinfmt.h>
19 #include <NPUdrvAPI.h>
20 #include <CommPlugin.h>
21
22 #include <string.h>
23 #include <assert.h>
24
25 #include <condition_variable>
26 #include <functional>
27 #include <atomic>
28 #include <map>
29
30 #define TAG _N2
31
32 /** @brief host handler constructor */
33 HostHandler::HostHandler (Device *device)
34   : device_(device),
35     /* ignored as we don't use double buffering anymore, but for backward-compatibility */
36     async_mode_ (NPUASYNC_WAIT)
37 {
38 }
39
40 /** @brief host handler destructor */
41 HostHandler::~HostHandler ()
42 {
43 }
44
45 /**
46  * @brief register model from generic buffer
47  * @param[in] model_buf model buffer
48  * @param[out] modelid model id
49  * @return 0 if no error. otherwise a negative errno
50  */
51 int
52 HostHandler::registerModel (generic_buffer *model_buf, uint32_t *modelid)
53 {
54   if (model_buf == nullptr || modelid == nullptr) {
55     logerr (TAG, "Invalid arguments given\n");
56     return -EINVAL;
57   }
58
59   Model *model = nullptr;
60   int status = device_->setModel (model_buf, &model);
61   if (status != 0) {
62     logerr (TAG, "Failed to set model: %d\n", status);
63     return status;
64   }
65
66   assert (model != nullptr);
67
68   status = models_.insert (model->getID(), model);
69   if (status != 0) {
70     logerr (TAG, "Failed to insert model id\n");
71     delete model;
72     return status;
73   }
74
75   *modelid = model->getID();
76   return 0;
77 }
78
79 /**
80  * @brief remove the registered model
81  * @param[in] modelid model id
82  * @return 0 if no error. otherwise a negative errno
83  */
84 int
85 HostHandler::unregisterModel (uint32_t modelid)
86 {
87   Model *model = models_.find (modelid);
88   if (model == nullptr)
89     return -ENOENT;
90
91   int status = device_->unsetModel (model);
92   if (status != 0) {
93     logerr (TAG, "Failed to unset model: %d\n", status);
94     return status;
95   }
96
97   return models_.remove (modelid);
98 }
99
100 /**
101  * @brief remove all registered models
102  * @return 0
103  */
104 int
105 HostHandler::unregisterModels ()
106 {
107   std::function <bool (Model *)> functor =
108     [&] (Model *m) -> bool {
109       bool can_remove = true;
110       int status = device_->unsetModel (m);
111       if (status != 0) {
112         logwarn (TAG, "Failed to unset model: %d\n", status);
113         can_remove = false;
114       }
115       return can_remove;
116     };
117
118   models_.for_each (functor);
119   return 0;
120 }
121
122 /**
123  * @brief Get the profile information from NPU
124  * @param[in] task_id The identifier for each inference
125  * @param[out] profile The profile instance
126  * @return 0 if no error, otherwise a negative errno.
127  */
128 int
129 HostHandler::getProfile (int task_id, npu_profile *profile)
130 {
131   if (task_id < 0 || profile == nullptr) {
132     logerr (TAG, "Invalid parameter provided\n");
133     return -EINVAL;
134   }
135
136   const DriverAPI * api = device_->getDriverAPI ();
137   assert (api != nullptr);
138
139   void *profile_buffer;
140   size_t profile_size;
141   int status = api->getProfile (task_id, &profile_buffer, &profile_size);
142   if (status != 0) {
143     logerr (TAG, "Failed to get profile information: %d\n", status);
144     return status;
145   }
146
147   profile->num_layers = 0;
148   profile->layers = nullptr;
149   if (profile_buffer != nullptr) {
150     // TODO: Perform parsing
151   }
152
153   return 0;
154 }
155
156 /**
157  * @brief get the stats for the latest apps of the target device
158  * @param[out] stat The list of app stat
159  * @note The caller has the responsibility to free the resources.
160  *       This API is not working on the emulated envionment.
161  */
162 int
163 HostHandler::getStatApps (npu_stat_apps *stat)
164 {
165   const DriverAPI * api = device_->getDriverAPI ();
166   assert (api != nullptr);
167
168   return api->getStatApps (stat);
169 }
170
171 /**
172  * @brief get the stats for the latest tasks of the target app
173  * @param[in] appid The identifier of target app
174  * @param[out] stat The list of task stat
175  * @note The caller has the responsibility to free the resources.
176  *       This API is not working on the emulated envionment.
177  */
178 int
179 HostHandler::getStatTasks (int appid, npu_stat_tasks *stat)
180 {
181   const DriverAPI * api = device_->getDriverAPI ();
182   assert (api != nullptr);
183
184   return api->getStatTasks (appid, stat);
185 }
186
187 /**
188  * @brief Get the driver API level of opened NPU device
189  * @param[out] level driver API level
190  * @return 0 if no error, otherwise a negative errno
191  */
192 int
193 HostHandler::getAPILevel (uint32_t *level)
194 {
195   const DriverAPI * api = device_->getDriverAPI ();
196   assert (api != nullptr);
197
198   return api->getAPILevel (level);
199 }
200
201 /**
202  * @brief Get the TOPS of the opened NPU device
203  * @param[in] dev the NPU device handle
204  * @param[out] tops npu tops
205  * @return 0 if no error, otherwise a negative errno
206  * @note this does not support for emulated devices
207  */
208 int
209 HostHandler::getTops (uint32_t *tops)
210 {
211   const DriverAPI * api = device_->getDriverAPI ();
212   assert (api != nullptr);
213
214   return api->getTops (tops);
215 }
216
217 /**
218  * @brief Set the data layout for input/output tensors
219  * @param[in] modelid The ID of model whose layouts are set
220  * @param[in] in the layout/type info for input tensors
221  * @param[in] out the layout/type info for output tensors
222  * @return @c 0 if no error. otherwise a negative error value
223  * @note if this function is not called, default layout/type will be used.
224  */
225 int
226 HostHandler::setDataInfo (uint32_t modelid, tensors_data_info *in,
227     tensors_data_info *out)
228 {
229   Model *model = models_.find (modelid);
230   if (model == nullptr)
231     return -ENOENT;
232
233   return model->setDataInfo (in, out);
234 }
235
236 /**
237  * @brief Set the inference constraint for next NPU inferences
238  * @param[in] modelid The target model id
239  * @param[in] constraint inference constraint (e.g., timeout, priority)
240  * @return @c 0 if no error. otherwise a negative error value
241  * @note If this function is not called, default values are used.
242  */
243 int
244 HostHandler::setConstraint (uint32_t modelid, npuConstraint constraint)
245 {
246   Model *model = models_.find (modelid);
247   if (model == nullptr)
248     return -ENOENT;
249
250   model->setConstraint (constraint);
251
252   return 0;
253 }
254
255 /**
256  * @brief find and return model instance
257  * @param[in] modelid model id
258  * @return model instance if found. otherwise nullptr
259  */
260 Model *
261 HostHandler::getModel (uint32_t modelid)
262 {
263   return models_.find (modelid);
264 }
265
266 /** @brief dummay callback for runSync. */
267 class callbackSync {
268   public:
269     callbackSync (output_buffers *output) : output_(output), done_(false) {}
270
271     static void callback (output_buffers *output, uint64_t sequence, void *data) {
272       callbackSync *sync = static_cast<callbackSync *>(data);
273       sync->callback (output, sequence);
274     }
275
276     void callback (output_buffers *output, uint64_t sequence) {
277       if (output_ != nullptr) {
278         /** just copy internal variables of output buffers */
279         memcpy (output_, output, sizeof (output_buffers));
280       }
281       done_ = true;
282       cv_.notify_one ();
283     }
284
285     void wait () {
286       std::unique_lock<std::mutex> lock (m_);
287       cv_.wait (lock, [this]() { return done_; });
288     }
289
290   private:
291     std::mutex m_;
292     std::condition_variable cv_;
293     output_buffers *output_;
294     bool done_;
295 };
296
297 /**
298  * @brief Execute inference. Wait (block) until the output is available.
299  * @param[in] modelid The model to be inferred.
300  * @param[in] input The input data to be inferred.
301  * @param[out] output The output result.
302  * @return @c 0 if no error. otherwise a negative error value
303  */
304 int
305 HostHandler::runSync (uint32_t modelid, const input_buffers *input,
306     output_buffers *output)
307 {
308   callbackSync sync (output);
309   int status = runAsync (modelid, input, callbackSync::callback,
310       static_cast <void*> (&sync), NPUASYNC_DROP_OLD, nullptr);
311   if (status == 0) {
312     /** sync needs to wait callback */
313     sync.wait ();
314   }
315   return status;
316 }
317
318 /**
319  * @brief Invoke NPU inference. Unblocking call.
320  * @param[in] modelid The model to be inferred.
321  * @param[in] input The input data to be inferred.
322  * @param[in] cb The output buffer handler.
323  * @param[in] cb_data The data given as a parameter to the runNPU_async call.
324  * @param[in] mode Configures how this operation works.
325  * @param[out] sequence The sequence number returned with runNPU_async.
326  * @return @c 0 if no error. otherwise a negative error value
327  */
328 int
329 HostHandler::runAsync (uint32_t modelid, const input_buffers *input,
330     npuOutputNotify cb, void *cb_data, npu_async_mode mode, uint64_t *sequence)
331 {
332   Model *model = nullptr;
333
334   if (device_->needModel()) {
335     model = getModel (modelid);
336     if (model == nullptr)
337       return -ENOENT;
338   }
339
340   /* check the given model before running */
341   if (model != nullptr && !model->finalize ()) {
342     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
343     return -EINVAL;
344   }
345
346   device_->setAsyncMode (mode);
347   return device_->run (NPUINPUT_HOST, model, input, cb, cb_data, sequence);
348 }
349
350 /**
351  * @brief Let NPU accept input frames from its internal source continuously
352  * @param[in] modelid The model to be inferred.
353  * @param[in] opmode NPU has different opmode with auto-inputs. Choose one.
354  * @param[in] hw_dev The target device feeding input data
355  * @return @c 0 if no error. otherwise a negative error value
356  */
357 int
358 HostHandler::runInternal (uint32_t modelid, npu_input_opmode opmode,
359     std::string hw_dev)
360 {
361   Model *model = nullptr;
362
363   if (device_->needModel()) {
364     model = getModel (modelid);
365     if (model == nullptr)
366       return -ENOENT;
367   }
368
369   /* check the given model before running */
370   if (model != nullptr && !model->finalize ()) {
371     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
372     return -EINVAL;
373   }
374
375   return device_->runInternal (opmode, model, hw_dev);
376 }
377
378 /**
379  * @brief Stop the request with the given id
380  * @param[in] dev The NPU device handle
381  * @param[in] id The request id
382  * @return @c 0 if no error. otherwise a negative error value
383  */
384 int
385 HostHandler::stopInternal (int id)
386 {
387   if (id <= 0) {
388     logerr (TAG, "Unable to stop this request with id (%d)\n", id);
389     return -EINVAL;
390   }
391
392   const DriverAPI * api = device_->getDriverAPI ();
393   assert (api != nullptr);
394
395   return api->stop_target (id);
396 }
397
398 /**
399  * @brief get number of available devices
400  * @param[in] type device type
401  * @return number of devices
402  */
403 int
404 HostHandler::getNumDevices (dev_type type)
405 {
406   return DriverAPI::getNumDevices (type);
407 }
408
409 /**
410  * @brief get device instance
411  * @param[out] dev device instance
412  * @param[in] type device type
413  * @param[in] id device id
414  * @return 0 if no error. otherwise a negative errno
415  */
416 int
417 HostHandler::getDevice (npudev_h *dev, dev_type type, uint32_t id)
418 {
419   int num_devices = getNumDevices (type);
420
421   /** check the validity of device id */
422   if (!(num_devices > 0 && id < static_cast<uint32_t>(num_devices))) {
423     logerr (TAG, "Invalid arguments provided\n");
424     return -ENODEV;
425   }
426
427   Device *device = Device::createInstance (type, id);
428   if (device == nullptr) {
429     logerr (TAG, "Failed to create a device with the given type\n");
430     return -EINVAL;
431   }
432
433   *dev = device;
434
435   return 0;
436 }
437
438 /**
439  * @brief allocate generic buffer (just for users)
440  * @param[out] buffer buffer instance
441  * @return 0 if no error. otherwise a negative errno
442  */
443 int
444 HostHandler::allocGenericBuffer (generic_buffer *buffer)
445 {
446   if (buffer == NULL)
447     return -EINVAL;
448
449   if (buffer->size == 0) {
450     logerr (TAG, "Invalid size\n");
451     return -EINVAL;
452   }
453
454   if (buffer->size > UINT32_MAX) {
455     logerr (TAG, "Don't support such a large size");
456     return -ENOMEM;
457   }
458
459   switch (buffer->type) {
460     case BUFFER_FILE:
461       /* nothing to do */
462       if (buffer->filepath == nullptr)
463         return -EINVAL;
464       break;
465     case BUFFER_MAPPED:
466     {
467       /* now, npu-engine always provides dmabuf-based allocation */
468       void *addr = nullptr;
469       int dmabuf = device_->allocMemory (buffer->size, &addr);
470       if (dmabuf < 0)
471         return dmabuf;
472
473       buffer->dmabuf = dmabuf;
474       buffer->offset = 0;
475       buffer->addr = addr;
476     } break;
477     default:
478       return -EINVAL;
479   }
480
481   return 0;
482 }
483
484 /**
485  * @brief deallocate generic buffer (just for users)
486  * @param[in] buffer buffer instance
487  * @return 0 if no error. otherwise a negative errno
488  */
489 int
490 HostHandler::deallocGenericBuffer (generic_buffer *buffer)
491 {
492   if (buffer == NULL)
493     return -EINVAL;
494
495   switch (buffer->type) {
496     case BUFFER_FILE:
497       /** always true cuz nothing to do */
498       break;
499     case BUFFER_MAPPED:
500       return device_->deallocMemory (buffer->dmabuf, buffer->size, buffer->addr);
501     default:
502       return -EINVAL;
503   }
504
505   return 0;
506 }
507
508 /**
509  * @brief allocate multiple generic buffers (just for users)
510  * @param[out] buffers multi-buffer instance
511  * @return 0 if no error. otherwise a negative errno
512  */
513 int
514 HostHandler::allocGenericBuffer (generic_buffers *buffers)
515 {
516   uint32_t idx;
517   int status = 0;
518
519   if (buffers == NULL || buffers->num_buffers < 1)
520     return -EINVAL;
521
522   for (idx = 0; idx < buffers->num_buffers; idx++) {
523     status = allocGenericBuffer (&buffers->bufs[idx]);
524     if (status != 0)
525       goto free_buffer;
526   }
527
528   return 0;
529
530 free_buffer:
531   while (idx != 0) {
532     deallocGenericBuffer (&buffers->bufs[--idx]);
533   }
534
535   return status;
536 }
537
538 /**
539  * @brief deallocate multiple generic buffers (just for users)
540  * @param[in] buffers multi-buffer instance
541  * @return 0 if no error. otherwise a negative errno
542  */
543 int
544 HostHandler::deallocGenericBuffer (generic_buffers *buffers)
545 {
546   if (buffers == NULL || buffers->num_buffers < 1)
547     return -EINVAL;
548
549   for (uint32_t idx = 0; idx < buffers->num_buffers; idx++)
550     deallocGenericBuffer (&buffers->bufs[idx]);
551   buffers->num_buffers = 0;
552
553   return 0;
554 }
555
556 /**
557  * @brief get the current memory status
558  * @param[out] alloc_total The size of allocated memory until now
559  * @param[out] free_total The size of freed memory until now
560  * @return 0 if no error. otherwise a negatice error value
561  */
562 int
563 HostHandler::getMemoryStatus (size_t *alloc_total, size_t *free_total)
564 {
565   /** API is always set in initialize () */
566   const DriverAPI * api = device_->getDriverAPI ();
567   assert (api != nullptr);
568
569   return api->getMemoryStatus (alloc_total, free_total);
570 }
571
572 /**
573  * @brief Get the current device status to be used
574  * @param[out] status the device status
575  * @param[out] num_requests the number of running requests (or pending)
576  * @return 0 if no error, otherwise a negative errno.
577  */
578 int
579 HostHandler::getDeviceStatus (npu_status *status, uint32_t *num_requests)
580 {
581   /** API is always set in initialize () */
582   const DriverAPI * api = device_->getDriverAPI ();
583
584   if (!api)
585     return -EINVAL;
586
587   device_state_t state = api->isReady ();
588   if (state == device_state_t::STATE_READY) {
589     *num_requests = api->numRequests ();
590     if (*num_requests > 0)
591       *status = NPU_READY;
592     else
593       *status = NPU_IDLE;
594   } else {
595     *num_requests = 0;
596     *status = NPU_ERROR;
597   }
598
599   return 0;
600 }
601
602 /** implement methods of Device class */
603
604 /** @brief constructor of device */
605 Device::Device (dev_type type, int id, bool need_model)
606   : comm_ (CommPlugin::getCommPlugin()), type_ (type), id_ (id), need_model_ (true),
607     mode_ (NPUASYNC_WAIT), initialized_ (false), atomic_flag_ (ATOMIC_FLAG_INIT)
608 {
609 }
610
611 /**
612  * @brief create device instance depending on device type and id
613  * @param[in] type device type
614  * @param[in] id device id
615  * @return device instance
616  */
617 Device *
618 Device::createInstance (dev_type type, int id)
619 {
620   Device *device = nullptr;
621
622   switch (type & DEVICETYPE_MASK) {
623     case DEVICETYPE_TRIV:
624       device = new TrinityVision (id);
625       break;
626     case DEVICETYPE_TRIV2:
627       device = new TrinityVision2 (id);
628       break;
629     case DEVICETYPE_TRIA:
630       device = new TrinityAsr (id);
631       device->setNeedModel (false);
632       break;
633     default:
634       break;
635   }
636
637   if (device != nullptr && device->init () != 0) {
638     delete device;
639     device = nullptr;
640   }
641
642   return device;
643 }
644
645 /**
646  * @brief device initialization
647  * @return 0 if no error, otherwise a negative errno
648  * @note Init failures come from createDriverAPI() only.
649  */
650 int
651 Device::init ()
652 {
653   /** should be initilizaed only once */
654   if (!atomic_flag_.test_and_set()) {
655     /** create the corresponding driver API */
656     api_ = DriverAPI::createDriverAPI (type_, id_);
657     if (api_.get() == nullptr) {
658       atomic_flag_.clear();
659       logerr (TAG, "Failed to create driver API\n");
660       return -EINVAL;
661     }
662
663     handler_.reset (new HostHandler (this));
664     scheduler_.reset (new Scheduler (api_.get()));
665     mem_ = MemAllocator::createInstance (api_.get());
666
667     initialized_ = true;  /** c++11 does not provide test() of atomic flag */
668   }
669
670   return 0;
671 }
672
673 /**
674  * @brief stop all requests from this device
675  * @param[in] force_stop indicate the schedduler waits until to handle previous requests
676  * @return 0 if no error, otherwise a negative errno
677  */
678 int
679 Device::stop (bool force_stop)
680 {
681   if (!initialized ()) {
682     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
683     return -EPERM;
684   }
685
686   Request *req = new Request (NPUINPUT_STOP);
687   req->setForceStop (force_stop);
688   return scheduler_->submitRequest (req);
689 }
690
691 /**
692  * @brief allocate generic memory buffer
693  * @param[in] size the size to allocate
694  * @param[out] addr the mapped address
695  * @return dmabuf fd if no error, otherwise a negative errno
696  */
697 int
698 Device::allocMemory (size_t size, void **addr)
699 {
700   if (!initialized ()) {
701     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
702     return -EPERM;
703   }
704
705   if (size == 0 || addr == nullptr) {
706     logerr (TAG, "Invalid arguments\n");
707     return -EINVAL;
708   }
709
710   return mem_->allocMemory (size, addr);
711 }
712
713 /**
714  * @brief deallocate generic memory buffer
715  * @param[in] dmabuf_fd dmabuf file descriptor
716  * @param[in] size buffer size
717  * @param[in] addr mapped addr
718  * @return 0 if no error, otherwise a negative errno
719  */
720 int
721 Device::deallocMemory (int dmabuf_fd, size_t size, void * addr)
722 {
723   if (!initialized ()) {
724     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
725     return -EPERM;
726   }
727
728   if (dmabuf_fd < 0 || size == 0 || addr == nullptr) {
729     logerr (TAG, "Invalid arguments\n");
730     return -EINVAL;
731   }
732
733   return mem_->deallocMemory (dmabuf_fd, size, addr);
734 }
735
736 /**
737  * @brief extract the buffer instance from input generic buffers
738  * @param[in] meta the model metadata
739  * @param[in] input the input generic buffers
740  * @return the buffer instance
741  */
742 Buffer *
743 TrinityVision::prepareInputBuffers (const Metadata *meta, const input_buffers *input)
744 {
745   if (meta == nullptr || input == nullptr ||
746       meta->getInputNum() != input->num_buffers) {
747     logerr (TAG, "Invalid metadata info provided\n");
748     return nullptr;
749   }
750
751   Buffer * buffer;
752   const generic_buffer *first = &input->bufs[0];
753   if (first->type == BUFFER_DMABUF) {
754     buffer = mem_->allocBuffer (new HWmemExternal);
755     if (buffer == nullptr)
756       return nullptr;
757
758     buffer->setDmabuf (first->dmabuf);
759     buffer->setOffset (first->offset);
760     buffer->setSize (meta->getBufferSize());
761   } else {
762     buffer = mem_->allocBuffer (new HWmemDevice);
763     if (buffer == nullptr)
764       return nullptr;
765
766     int status = buffer->alloc (meta->getBufferSize ());
767     if (status != 0) {
768       logerr (TAG, "Failed to allocate buffer: %d\n", status);
769       delete buffer;
770       return nullptr;
771     }
772   }
773
774   int status = buffer->createTensors (meta);
775   if (status != 0) {
776     logerr (TAG, "Failed to create tensors: %d\n", status);
777     delete buffer;
778     buffer = nullptr;
779   }
780
781   return buffer;
782 }
783
784 /**
785  * @brief implementation of TRIV's setModel ()
786  * @param[in] model_buf the model generic buffer
787  * @param[out] model the model instance
788  * @return 0 if no error, otherwise a negative errno
789  */
790 int
791 TrinityVision::setModel (const generic_buffer *model_buf, Model ** model_ptr)
792 {
793   if (!initialized ()) {
794     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
795     return -EPERM;
796   }
797
798   if (model_buf == nullptr || model_ptr == nullptr)
799     return -EINVAL;
800
801   Model *model = nullptr;
802   HWmem * hwmem_prog = nullptr;
803   HWmem * hwmem_weight = nullptr;
804   int status;
805
806   /** In TRIV1, model data (including program/weight) should be contiguous */
807
808   switch (model_buf->type) {
809   case BUFFER_FILE:
810   case BUFFER_MAPPED:
811     model = mem_->allocModel (new HWmemDevice);
812     if (model == nullptr) {
813       logerr (TAG, "Failed to allocate model\n");
814       return -ENOMEM;
815     }
816
817     status = model->alloc (model_buf->size);
818     if (status != 0) {
819       logerr (TAG, "Failed to allocate model: %d\n", status);
820       goto delete_exit;
821     }
822
823     /** extract the whole model data */
824     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr);
825     if (status != 0) {
826       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
827       goto delete_exit;
828     }
829     break;
830   default:
831     return -EINVAL;
832   }
833
834   status = model->setMetadata (model->getData());
835   if (status != 0)
836     goto delete_exit;
837
838   /** allocate program (optional; NOP) */
839   if (model->getMetadata()->getProgramSize() > 0) {
840     hwmem_prog = new HWmem (new HWmemChunk);
841     model->setProgramData (hwmem_prog);
842
843     hwmem_prog->setParent (model);
844     hwmem_prog->setOffset (model->getMetadata()->getMetaSize());
845     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
846     if (status != 0) {
847       logerr (TAG, "Failed to allocate program\n");
848       goto delete_exit;
849     }
850   }
851
852   /** allocate weight (optional) */
853   if (model->getMetadata()->getWeightSize() > 0) {
854     hwmem_weight = new HWmem (new HWmemChunk);
855     model->setWeightData (hwmem_weight);
856
857     hwmem_weight->setParent (model);
858     hwmem_weight->setOffset (model->getMetadata()->getMetaSize() +
859         model->getMetadata()->getProgramSize());
860     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
861     if (status != 0) {
862       logerr (TAG, "Failed to allocate program\n");
863       goto delete_exit;
864     }
865   }
866
867   if (hwmem_prog != nullptr) {
868     /** register this model to the driver */
869     model_config_t config;
870     config.dbuf_fd = hwmem_prog->getDmabuf ();
871     config.program_size = hwmem_prog->getSize ();
872     config.program_offset_addr = hwmem_prog->getOffset ();
873     if (hwmem_weight != nullptr)
874       config.weight_offset_addr = hwmem_weight->getOffset ();
875
876     status = api_->registerModel (&config);
877     if (status != 0)
878       goto delete_exit;
879
880     model->setInternalID(config.id);
881   }
882
883   *model_ptr = model;
884   return status;
885
886 delete_exit:
887   delete model;
888   return status;
889 }
890
891 /**
892  * @brief implementation of TRIV's unsetModel ()
893  * @param[in] model the model instance
894  * @return 0 if no error, otherwise a negative errno
895  */
896 int
897 TrinityVision::unsetModel (Model * model)
898 {
899   if (!initialized ()) {
900     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
901     return -EPERM;
902   }
903
904   if (model == nullptr) {
905     logerr (TAG, "Invalid model instance\n");
906     return -EINVAL;
907   }
908
909   if (model->getMetadata()->getProgramSize() > 0)
910     return api_->deregisterModel (model->getInternalID ());
911
912   return 0;
913 }
914
915 /**
916  * @brief implementation of TRIV's run()
917  * @param[in] opmode input opmode
918  * @param[in] model the model instance
919  * @param[in] input generic buffers of input data
920  * @param[in] cb the output callback
921  * @param[in] cb_data the output callback data
922  * @param[out] sequence The sequence number returned with runNPU_async.
923  */
924 int
925 TrinityVision::run (npu_input_opmode opmode, const Model *model,
926     const input_buffers *input, npuOutputNotify cb, void *cb_data,
927     uint64_t *sequence)
928 {
929   if (!initialized ()) {
930     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
931     return -EPERM;
932   }
933
934   if (opmode != NPUINPUT_HOST) {
935     logerr (TAG, "TRIV supports only host inputservice\n");
936     return -EINVAL;
937   }
938
939   if (model == nullptr || input == nullptr) {
940     logerr (TAG, "TRIV requires both model and input buffers\n");
941     return -EINVAL;
942   }
943
944   const_cast<Model *>(model)->updateDataInfo ();
945
946   Buffer *buffer = prepareInputBuffers (model->getMetadata(), input);
947   if (buffer == nullptr) {
948     logerr (TAG, "Failed to extract buffer instance\n");
949     return -EINVAL;
950   }
951
952   if (!buffer->isExternal ()) {
953     for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
954       auto func = std::bind (TrinityVision::manipulateData, model, idx, true,
955           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
956       int status = comm_.extractGenericBuffer (&input->bufs[idx],
957           buffer->getInputTensor(idx)->getData(), func);
958       if (status != 0) {
959         logerr (TAG, "Failed to feed input buffer: %d\n", status);
960         return status;
961       }
962     }
963   }
964
965   /** this device uses CMA buffer */
966
967   Request *req = new Request (opmode);
968   req->setModel (model);
969   req->setBuffer (buffer);
970
971   if (cb != nullptr)
972     req->setCallback (std::bind (&TrinityVision::callback, this, req, cb, cb_data));
973
974   if (sequence != nullptr)
975     *sequence = req->getID();
976
977   return scheduler_->submitRequest (req);
978 }
979
980 /**
981  * @brief callback of TRIV2 request
982  * @param[in] req the request instance
983  * @param[in] cb callback for completion
984  * @param[in] cb_data callback data
985  * @note The callback invoke does not gurantee the request was successful
986  * @todo Check the request failures
987  */
988 void
989 TrinityVision::callback (Request *req, npuOutputNotify cb, void *cb_data)
990 {
991   const Model *model = req->getModel ();
992   Buffer *buffer = req->getBuffer ();
993   output_buffers output = {
994     .num_buffers = buffer->getOutputNum ()
995   };
996
997   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
998     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
999
1000     if (buffer->isExternal ()) {
1001       output.bufs[idx].type = BUFFER_DMABUF;
1002       output.bufs[idx].size = output_tensor_size;
1003       output.bufs[idx].addr = buffer->getOutputTensor(idx)->getData();
1004     } else {
1005       output.bufs[idx].type = BUFFER_MAPPED;
1006       output.bufs[idx].size = output_tensor_size;
1007       /** user needs to free this */
1008       output.bufs[idx].addr = malloc (output_tensor_size);
1009
1010       auto func = std::bind (TrinityVision::manipulateData, model, idx, false,
1011           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1012       int status = comm_.insertGenericBuffer (buffer->getOutputTensor(idx)->getData(),
1013           &output.bufs[idx], func);
1014       if (status != 0) {
1015         logerr (TAG, "Failed to return output buffer: %d\n", status);
1016       }
1017     }
1018   }
1019
1020   cb (&output, req->getID(), cb_data);
1021
1022   delete buffer;
1023 }
1024
1025 /**
1026  * @brief extract the segment table instance from input generic buffers
1027  * @param[in] model the model instance
1028  * @param[in] input the input generic buffers
1029  * @param[in] output the output generic buffers
1030  * @return the segment table instance
1031  */
1032 SegmentTable *
1033 TrinityVision2::prepareSegmentTable (const Model *model, const input_buffers *input,
1034     const output_buffers *output)
1035 {
1036   const Metadata *meta = model->getMetadata ();
1037   if (meta == nullptr || (input != nullptr &&
1038         meta->getInputNum() != input->num_buffers)) {
1039     logerr (TAG, "Invalid metadata info provided\n");
1040     return nullptr;
1041   }
1042
1043   SegmentTable * segt = mem_->allocSegmentTable (new HWmemDevice);
1044   int status = segt->alloc ();
1045   if (status != 0) {
1046     logerr (TAG, "Failed to allocate segment table: %d\n", status);
1047     goto delete_segt;
1048   }
1049
1050   status = segt->createSegments (model, input, output);
1051   if (status != 0) {
1052     logerr (TAG, "Failed to create segments: %d\n", status);
1053     goto delete_segt;
1054   }
1055
1056   return segt;
1057
1058 delete_segt:
1059   delete segt;
1060   return nullptr;
1061 }
1062
1063 /**
1064  * @brief implementation of TRIV2's setModel ()
1065  * @param[in] model_buf the model generic buffer
1066  * @param[out] model the model instance
1067  * @return 0 if no error, otherwise a negative errno
1068  */
1069 int
1070 TrinityVision2::setModel (const generic_buffer *model_buf, Model ** model_ptr)
1071 {
1072   if (!initialized ()) {
1073     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1074     return -EPERM;
1075   }
1076
1077   if (model_buf == nullptr || model_ptr == nullptr)
1078     return -EINVAL;
1079
1080   Model *model;
1081   int status;
1082
1083   switch (model_buf->type) {
1084   case BUFFER_FILE:
1085   case BUFFER_MAPPED:
1086     model = mem_->allocModel (new HWmemDevice);
1087     if (model == nullptr) {
1088       logerr (TAG, "Failed to allocate model\n");
1089       return -ENOMEM;
1090     }
1091
1092     status = model->alloc (NPUBIN_META_SIZE);
1093     if (status != 0) {
1094       logerr (TAG, "Failed to allocate model: %d\n", status);
1095       goto delete_exit;
1096     }
1097
1098     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr,
1099         0, NPUBIN_META_SIZE);
1100     if (status != 0) {
1101       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1102       goto delete_exit;
1103     }
1104     break;
1105   default:
1106     return -EINVAL;
1107   }
1108
1109   status = model->setMetadata (model->getData());
1110   if (status != 0)
1111     goto delete_exit;
1112
1113   /** allocate program (optional; NOP) */
1114   if (model->getMetadata()->getProgramSize() > 0) {
1115     HWmem * hwmem_prog = new HWmem (new HWmemDevice);
1116     hwmem_prog->setDriverAPI (api_.get());
1117
1118     model->setProgramData (hwmem_prog);
1119
1120     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
1121     if (status != 0) {
1122       logerr (TAG, "Failed to allocate program\n");
1123       goto delete_exit;
1124     }
1125
1126     status = comm_.extractGenericBuffer (model_buf, hwmem_prog->getData(), nullptr,
1127         model->getMetadata()->getMetaSize(),
1128         model->getMetadata()->getProgramSize());
1129     if (status != 0) {
1130       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1131       goto delete_exit;
1132     }
1133
1134     /** register this model to the driver */
1135     model_config_t config;
1136     config.dbuf_fd = hwmem_prog->getDmabuf ();
1137     config.program_size = hwmem_prog->getSize ();
1138     config.program_offset_addr = 0;
1139
1140     /** for metadata extra section */
1141     config.metadata_dbuf_fd = model->getDmabuf ();
1142     config.metadata_extra_addr = NPUBIN_META_SIZE;
1143     config.metadata_extra_size = model->getMetadata()->getMetaExtraSize ();
1144
1145     status = api_->registerModel (&config, model->getMetadata()->getNPUVersion());
1146     if (status != 0)
1147       goto delete_exit;
1148
1149     model->setInternalID(config.id);
1150   }
1151
1152   /** allocate weight (optional) */
1153   if (model->getMetadata()->getWeightSize() > 0) {
1154     HWmem * hwmem_weight = new HWmem (new HWmemDevice);
1155     hwmem_weight->setDriverAPI (api_.get());
1156
1157     model->setWeightData (hwmem_weight);
1158
1159     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
1160     if (status != 0) {
1161       logerr (TAG, "Failed to allocate program\n");
1162       goto delete_exit;
1163     }
1164
1165     status = comm_.extractGenericBuffer (model_buf, hwmem_weight->getData(), nullptr,
1166         model->getMetadata()->getMetaSize() + model->getMetadata()->getProgramSize(),
1167         model->getMetadata()->getWeightSize());
1168     if (status != 0) {
1169       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1170       goto delete_exit;
1171     }
1172   }
1173
1174   *model_ptr = model;
1175   return status;
1176
1177 delete_exit:
1178   delete model;
1179   return status;
1180 }
1181
1182 /**
1183  * @brief implementation of TRIV2's unsetModel ()
1184  * @param[in] model the model instance
1185  * @return 0 if no error, otherwise a negative errno
1186  */
1187 int
1188 TrinityVision2::unsetModel (Model * model)
1189 {
1190   if (!initialized ()) {
1191     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1192     return -EPERM;
1193   }
1194
1195   if (model == nullptr) {
1196     logerr (TAG, "Invalid model instance\n");
1197     return -EINVAL;
1198   }
1199
1200   if (model->getMetadata()->getProgramSize() > 0)
1201     return api_->deregisterModel (model->getInternalID ());
1202
1203   return 0;
1204 }
1205
1206 /** @brief implementation of TRIV2's run() */
1207 int
1208 TrinityVision2::run (npu_input_opmode opmode, const Model *model,
1209     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1210     uint64_t *sequence)
1211 {
1212   if (!initialized ()) {
1213     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1214     return -EPERM;
1215   }
1216
1217   if (opmode != NPUINPUT_HOST)
1218     return -EINVAL;
1219
1220   if (input == nullptr || input->num_buffers == 0 || model == nullptr)
1221     return -EINVAL;
1222
1223   const_cast<Model *>(model)->updateDataInfo ();
1224
1225   /** this device uses segment table */
1226   SegmentTable * segt = prepareSegmentTable (model, input);
1227   if (segt == nullptr) {
1228     logerr (TAG, "Failed to create segment table instance\n");
1229     return -EINVAL;
1230   }
1231
1232   /** extract input data */
1233   for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
1234     if (!segt->getInputSegment(idx)->isExternal ()) {
1235       uint32_t seg_offset = segt->getInputSegmentOffset(idx);
1236       auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
1237           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1238       int status = comm_.extractGenericBuffer (
1239           &input->bufs[idx],
1240           segt->getInputSegment(idx)->getData() + seg_offset,
1241           func);
1242       if (status != 0) {
1243         logerr (TAG, "Failed to feed input segment: %d\n", status);
1244         return status;
1245       }
1246     }
1247   }
1248
1249   Request *req = new Request (opmode);
1250   req->setModel (model);
1251   req->setSegmentTable (segt);
1252   req->setCallback (std::bind (&TrinityVision2::callback, this, req, cb, cb_data));
1253
1254   if (sequence)
1255     *sequence = req->getID();
1256
1257   return scheduler_->submitRequest (req);
1258 }
1259
1260 /** @brief implementation of TRIV2's runInternal() */
1261 int
1262 TrinityVision2::runInternal (npu_input_opmode opmode, const Model *model,
1263     std::string hw_dev)
1264 {
1265   if (!initialized ()) {
1266     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1267     return -EPERM;
1268   }
1269
1270   if (opmode != NPUINPUT_HW_RECURRING)
1271     return -EINVAL;
1272
1273   /** this device uses segment table */
1274   SegmentTable * segt = prepareSegmentTable (model, nullptr, nullptr);
1275   if (segt == nullptr) {
1276     logerr (TAG, "Failed to create segment table instance\n");
1277     return -EINVAL;
1278   }
1279
1280   Request *req = new Request (opmode);
1281   req->setModel (model);
1282   req->setSegmentTable (segt);
1283   req->setHwDevice (hw_dev);
1284
1285   return scheduler_->submitRequest (req);
1286 }
1287
1288 /** @brief callback of TRIV2 request */
1289 void
1290 TrinityVision2::callback (Request *req, npuOutputNotify cb, void *cb_data)
1291 {
1292   const Model *model = req->getModel ();
1293   SegmentTable *segt = req->getSegmentTable ();
1294   output_buffers output = {
1295     .num_buffers = segt->getNumOutputSegments ()
1296   };
1297
1298   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
1299     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
1300
1301     output.bufs[idx].type = BUFFER_MAPPED;
1302     output.bufs[idx].size = output_tensor_size;
1303     /** user needs to free this */
1304     output.bufs[idx].addr = calloc (1, output_tensor_size);
1305
1306 #if defined(ENABLE_FPGA_WORKAROUND)
1307     api_->fpga_memcpy (
1308         segt->getOutputSegment(idx)->getDmabuf(),
1309         segt->getOutputSegmentOffset(idx),
1310         output.bufs[idx].addr,
1311         output.bufs[idx].size);
1312 #else
1313     auto func = std::bind (TrinityVision2::manipulateData, model, idx, false,
1314         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1315     int status = comm_.insertGenericBuffer (
1316         segt->getOutputSegment(idx)->getData() + segt->getOutputSegmentOffset(idx),
1317         &output.bufs[idx], func);
1318
1319     if (status != 0) {
1320       logerr (TAG, "Failed to return output buffer: %d\n", status);
1321     }
1322 #endif
1323   }
1324
1325   cb (&output, req->getID(), cb_data);
1326
1327   delete segt;
1328 }
1329
1330 /** @brief implementation of TRIA's run(): WIP */
1331 int
1332 TrinityAsr::run (npu_input_opmode opmode, const Model *model,
1333     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1334     uint64_t *sequence)
1335 {
1336   if (!initialized ()) {
1337     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1338     return -EPERM;
1339   }
1340
1341   if (opmode != NPUINPUT_HOST)
1342     return -EINVAL;
1343
1344   if (input == nullptr || input->num_buffers != 1)
1345     return -EINVAL;
1346
1347   Buffer * buffer;
1348   int status;
1349   /** ASR does not require model and support only a single tensor */
1350   const generic_buffer *first_buf = &input->bufs[0];
1351   if (first_buf->type == BUFFER_DMABUF) {
1352     buffer = mem_->allocBuffer (new HWmemExternal);
1353     if (buffer == nullptr)
1354       return -ENOMEM;
1355
1356     buffer->setDmabuf (first_buf->dmabuf);
1357     buffer->setOffset (first_buf->offset);
1358     buffer->setSize (first_buf->size);
1359   } else {
1360     buffer = mem_->allocBuffer (new HWmemDevice);
1361     if (buffer == nullptr)
1362       return -ENOMEM;
1363
1364     status = buffer->alloc (first_buf->size);
1365     if (status != 0) {
1366       delete buffer;
1367       return status;
1368     }
1369   }
1370
1371   status = buffer->createTensors ();
1372   if (status != 0) {
1373     logerr (TAG, "Failed to create tensors: %d\n", status);
1374     delete buffer;
1375     return status;
1376   }
1377
1378   if (!buffer->isExternal ()) {
1379     status = comm_.extractGenericBuffer (first_buf,
1380         buffer->getInputTensor(0)->getData(), nullptr);
1381     if (status != 0)
1382       return status;
1383   }
1384
1385   Request *req = new Request (opmode);
1386   req->setBuffer (buffer);
1387   req->setCallback (std::bind (&TrinityAsr::callback, this, req, cb, cb_data));
1388
1389   if (sequence)
1390     *sequence = req->getID();
1391
1392   return scheduler_->submitRequest (req);
1393 }
1394
1395 /** @brief callback of TRIA request: WIP */
1396 void
1397 TrinityAsr::callback (Request *req, npuOutputNotify cb, void *cb_data)
1398 {
1399   Buffer *buffer = req->getBuffer ();
1400   output_buffers output = {
1401     .num_buffers = 0
1402   };
1403
1404   /** TODO: finalize this impl. when the ASR's working scenario is determined */
1405   cb (&output, req->getID(), cb_data);
1406
1407   delete buffer;
1408 }
1409
1410 /** Implement data manipulation (each device may have different impl.) */
1411
1412 #ifdef ENABLE_MANIP
1413
1414 /**
1415  * @brief perform data manipulation
1416  * @param[in] model model instance
1417  * @param[in] idx tensor index
1418  * @param[in] is_input indicate it's input manipulation
1419  * @param[out] dst destination buffer
1420  * @param[in] src source buffer (feature map)
1421  * @param[in] size size to be copied
1422  * @return size of memory copy if no error, otherwise zero
1423  *
1424  * @note the input data format should be NHWC
1425  * @detail rules for the memory address of activations in NPU HW.
1426  *         (https://code.sec.samsung.net/confluence/pages/viewpage.action?pageId=146491864)
1427  *
1428  * 1) Special case (depth == 3)
1429  * - addr(x,y,z) = addr(0,0,0) + (z) + 3 * (x + width * y)
1430  *
1431  * 2) Common case
1432  * - addr(x,y,z) = addr(0,0,0) + (z % MPA_L) + MPA_L * (x + width * (y + height * (z / MPA_L)))
1433  *
1434  * Thus, if depth is not a multiple of MPA_L (i.e., 64), zero padding is required
1435  */
1436 size_t
1437 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
1438     void *dst, void *src, size_t size)
1439 {
1440   const Metadata *meta = model->getMetadata();
1441   DataConverter converter (is_input);
1442
1443   converter.setData (src, dst, size);
1444
1445   if (is_input) {
1446     const tensor_data_info* info = model->getInputDataInfo (idx);
1447     if (info == nullptr)
1448       return 0;
1449
1450     converter.setDataLayout (info->layout, DATA_LAYOUT_SRNPU);
1451     converter.setDataType (info->type, DATA_TYPE_SRNPU);
1452     converter.setDataDims (meta->getInputDims (idx));
1453     converter.setQuantZero (meta->getInputQuantZero (idx));
1454     converter.setQuantScale (meta->getInputQuantScale (idx));
1455   } else {
1456     const tensor_data_info* info = model->getOutputDataInfo (idx);
1457     if (info == nullptr)
1458       return 0;
1459
1460     converter.setDataLayout (DATA_LAYOUT_SRNPU, info->layout);
1461     converter.setDataType (DATA_TYPE_SRNPU, info->type);
1462     converter.setDataDims (meta->getOutputDims (idx));
1463     converter.setQuantZero (meta->getOutputQuantZero (idx));
1464     converter.setQuantScale (meta->getOutputQuantScale (idx));
1465   }
1466
1467   return converter.perform ();
1468 }
1469
1470 /**
1471  * @brief perform data manipulation
1472  * @param[in] model model instance
1473  * @param[in] idx tensor index
1474  * @param[in] is_input indicate it's input manipulation
1475  * @param[out] dst destination buffer
1476  * @param[in] src source buffer (feature map)
1477  * @param[in] size size to be copied
1478  * @return size of memory copy if no error, otherwise zero
1479  *
1480  * @note the input data format should be NHWC
1481  *
1482  * @detail Feature map data in TRIV2, (x, y, z) = (width, height, depth)
1483  *
1484  *         1) Image input (depth == 1 or depth == 3)
1485  *            Addr(x,y,z) = Addr(0,0,0) + z + depth * x + ymod * y
1486  *
1487  *         2) Common cases
1488  *            Addr(x,y,z) = Addr(0,0,0) + (z % 64) + (64 * x) + ymod * y + zmod * (z / 64)
1489  */
1490 size_t
1491 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1492     void *dst, void *src, size_t size)
1493 {
1494   const Metadata *meta = model->getMetadata();
1495   DataConverter converter (is_input);
1496
1497   converter.setData (src, dst, size);
1498
1499   if (is_input) {
1500     const tensor_data_info* info = model->getInputDataInfo (idx);
1501     if (info == nullptr)
1502       return 0;
1503
1504     converter.setDataLayout (info->layout, DATA_LAYOUT_TRIV2);
1505     converter.setDataType (info->type, meta->getInputQuantType (idx));
1506     converter.setDataDims (meta->getInputDims (idx));
1507     converter.setQuantZero (meta->getInputQuantZero (idx));
1508     converter.setQuantScale (meta->getInputQuantScale (idx));
1509   } else {
1510     const tensor_data_info* info = model->getOutputDataInfo (idx);
1511     if (info == nullptr)
1512       return 0;
1513
1514     converter.setDataLayout (DATA_LAYOUT_TRIV2, info->layout);
1515     converter.setDataType (meta->getOutputQuantType (idx), info->type);
1516     converter.setDataDims (meta->getOutputDims (idx));
1517     converter.setQuantZero (meta->getOutputQuantZero (idx));
1518     converter.setQuantScale (meta->getOutputQuantScale (idx));
1519   }
1520
1521   return converter.perform ();
1522 }
1523
1524 #else
1525
1526 size_t
1527 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
1528     void *dst, void *src, size_t size)
1529 {
1530   memcpy (dst, src, size);
1531   return size;
1532 }
1533
1534 size_t
1535 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1536     void *dst, void *src, size_t size)
1537 {
1538   memcpy (dst, src, size);
1539   return size;
1540 }
1541
1542 #endif
1543
1544 /** other device types don't have data manip impl. yet */
1545
1546 size_t
1547 TrinityAsr::manipulateData (const Model *model, uint32_t idx, bool is_input,
1548     void *dst, void *src, size_t size)
1549 {
1550   memcpy (dst, src, size);
1551   return size;
1552 }