[Bug/Unregister] Fix the bug related to unregisterModels()
[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-host-handler.cc
8  * @date 03 Apr 2020
9  * @brief Implementation of APIs to access NPU 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 <libnpuhost.h>
19 #include <npubinfmt.h>
20 #include <NPUdrvAPI.h>
21 #include <CommPlugin.h>
22
23 #include <string.h>
24 #include <assert.h>
25
26 #include <condition_variable>
27 #include <functional>
28 #include <atomic>
29 #include <map>
30
31 #define TAG _N2
32
33 #define INIT_HOST_HANDLER(handler, dev) \
34   Device *tdev = static_cast <Device *> (dev); \
35   if (tdev == nullptr) return -EINVAL; \
36   HostHandler *handler = tdev->getHostHandler (); \
37   if (handler == nullptr) return -EINVAL;
38
39 /** just for backward-compatability */
40 npudev_h HostHandler::latest_dev_ = nullptr;
41
42 /** implement libnpuhost APIs */
43
44 /**
45  * @brief Returns the number of available NPU devices.
46  * @return @c The number of NPU devices.
47  * @retval 0 if no NPU devices available. if positive (number of NPUs) if NPU devices available. otherwise, a negative error value.
48  * @note the caller should call putNPUdevice() to release the device handle
49  */
50 int getnumNPUdeviceByType (dev_type type)
51 {
52   return HostHandler::getNumDevices (type);
53 }
54
55 /**
56  * @brief Returns the handle of the chosen NPU devices.
57  * @param[out] dev The NPU device handle
58  * @param[in] id The NPU id to get the handle. 0 <= id < getnumNPUdeviceByType().
59  * @return @c 0 if no error. otherwise a negative error value
60  * @note the caller should call putNPUdevice() to release the device handle
61  */
62 int getNPUdeviceByType (npudev_h *dev, dev_type type, uint32_t id)
63 {
64   return HostHandler::getDevice (dev, type, id);
65 }
66
67 /**
68  * @brief release the NPU device instance obtained by getDevice ()
69  * @param[in] dev the NPU device handle
70  */
71 void putNPUdevice (npudev_h dev)
72 {
73   if (dev != nullptr)
74     delete static_cast<Device *> (dev);
75 }
76
77 /**
78  * @brief Get the profile information from NPU
79  * @param[in] dev The NPU device handle
80  * @param[in] run_id The identifier for each inference
81  * @param[out] profile The profile instance
82  * @return 0 if no error, otherwise a negative errno.
83  */
84 int getNPU_profile (npudev_h dev, int run_id, npu_profile *profile)
85 {
86   INIT_HOST_HANDLER (host_handler, dev);
87
88   return host_handler->getProfile (run_id, profile);
89 }
90
91 /**
92  * @brief Free the profile instance obtained by getNPU_profile().
93  * @param[in] profile The profile instance
94  */
95 void putNPU_profile (npu_profile *profile)
96 {
97   if (profile != nullptr)
98     free (profile);
99 }
100
101 /**
102  * @brief Send the NN model to NPU.
103  * @param[in] dev The NPU device handle
104  * @param[in] modelfile The filepath to the compiled NPU NN model in any buffer_type
105  * @param[out] modelid The modelid allocated for this instance of NN model.
106  * @return @c 0 if no error. otherwise a negative error value
107  *
108  * @detail For ASR devices, which do not accept models, but have models
109  *         embedded in devices, you do not need to call register and
110  *         register calls for ASR are ignored.
111  *
112  * @todo Add a variation: in-memory model register.
113  */
114 int registerNPUmodel (npudev_h dev, generic_buffer *modelfile, uint32_t *modelid)
115 {
116   INIT_HOST_HANDLER (host_handler, dev);
117
118   return host_handler->registerModel (modelfile, modelid);
119 }
120
121 /**
122  * @brief Remove the NN model from NPU
123  * @param[in] dev The NPU device handle
124  * @param[in] modelid The model to be removed from the NPU.
125  * @return @c 0 if no error. otherwise a negative error value
126  * @detail This may incur some latency with memory compatcion.
127  */
128 int unregisterNPUmodel(npudev_h dev, uint32_t modelid)
129 {
130   INIT_HOST_HANDLER (host_handler, dev);
131
132   return host_handler->unregisterModel (modelid);
133 }
134
135 /**
136  * @brief Remove all NN models from NPU
137  * @param[in] dev The NPU device handle
138  * @return @c 0 if no error. otherwise a negative error value
139  */
140 int unregisterNPUmodel_all(npudev_h dev)
141 {
142   INIT_HOST_HANDLER (host_handler, dev);
143
144   return host_handler->unregisterModels ();
145 }
146
147 /**
148  * @brief [OPTIONAL] Set the data layout for input/output tensors
149  * @param[in] dev The NPU device handle
150  * @param[in] modelid The ID of model whose layouts are set
151  * @param[in] info_in the layout/type info for input tensors
152  * @param[in] info_out the layout/type info for output tensors
153  * @return @c 0 if no error. otherwise a negative error value
154  * @note if this function is not called, default layout/type will be used.
155  */
156 int setNPU_dataInfo(npudev_h dev, uint32_t modelid,
157     tensors_data_info *info_in, tensors_data_info *info_out)
158 {
159   INIT_HOST_HANDLER (host_handler, dev);
160
161   return host_handler->setDataInfo (modelid, info_in, info_out);
162 }
163
164 /**
165  * @brief [OPTIONAL] Set the inference constraint for next NPU inferences
166  * @param[in] dev The NPU device handle
167  * @param[in] modelid The target model id
168  * @param[in] constraint inference constraint (e.g., timeout, priority)
169  * @return @c 0 if no error. otherwise a negative error value
170  * @note If this function is not called, default values are used.
171  */
172 int setNPU_constraint(npudev_h dev, uint32_t modelid, npuConstraint constraint)
173 {
174   INIT_HOST_HANDLER (host_handler, dev);
175
176   return host_handler->setConstraint (modelid, constraint);
177 }
178
179 /**
180  * @brief Execute inference. Wait (block) until the output is available.
181  * @param[in] dev The NPU device handle
182  * @param[in] modelid The model to be inferred.
183  * @param[in] input The input data to be inferred.
184  * @param[out] output The output result. The caller MUST allocate appropriately before calling this.
185  * @return @c 0 if no error. otherwise a negative error value
186  *
187  * @detail This is a syntactic sugar of runNPU_async().
188  *         CAUTION: There is a memcpy for the output buffer.
189  */
190 int runNPU_sync(npudev_h dev, uint32_t modelid, const input_buffers *input,
191     output_buffers *output)
192 {
193   INIT_HOST_HANDLER (host_handler, dev);
194
195   return host_handler->runSync (modelid, input, output);
196 }
197
198 /**
199  * @brief Invoke NPU inference. Unblocking call.
200  * @param[in] dev The NPU device handle
201  * @param[in] modelid The model to be inferred.
202  * @param[in] input The input data to be inferred.
203  * @param[in] cb The output buffer handler.
204  * @param[out] sequence The sequence number returned with runNPU_async.
205  * @param[in] data The data given as a parameter to the runNPU_async call.
206  * @param[in] mode Configures how this operation works.
207  * @return @c 0 if no error. otherwise a negative error value
208  */
209 int runNPU_async(npudev_h dev, uint32_t modelid, const input_buffers *input,
210     npuOutputNotify cb, uint64_t *sequence, void *data,
211     npu_async_mode mode)
212 {
213   INIT_HOST_HANDLER (host_handler, dev);
214
215   return host_handler->runAsync (modelid, input, cb, data, mode, sequence);
216 }
217
218 /**
219  * @brief Let NPU accept input frames from its internal source continuously
220  * @param[in] dev The NPU device handle
221  * @param[in] modelid The model to be inferred.
222  * @param[in] opmode NPU has different opmode with auto-inputs. Choose one.
223  * @param[in] hw_dev The target device feeding input data
224  * @return @c 0 if no error. otherwise a negative error value
225  */
226 int runNPU_internalInput(npudev_h dev, uint32_t modelid, npu_input_opmode opmode,
227     const char * hw_dev)
228 {
229   INIT_HOST_HANDLER (host_handler, dev);
230
231   return host_handler->runInternal (modelid, opmode, hw_dev);
232 }
233
234 /**
235  * @brief Stop the request with the given id
236  * @param[in] dev The NPU device handle
237  * @param[in] id The request id
238  * @return @c 0 if no error. otherwise a negative error value
239  */
240 int stopNPU_internalInput(npudev_h dev, int id)
241 {
242   INIT_HOST_HANDLER (host_handler, dev);
243
244   return host_handler->stopInternal (id);
245 }
246
247 /**
248  * @brief Allocate a generic buffer with the requested buffer type.
249  * @param[in] dev The NPU device handle
250  * @param[in/out] Buffer the buffer pointer where memory is allocated.
251  * @return 0 if no error, otherwise a negative errno.
252  */
253 int allocNPU_genericBuffer (npudev_h dev, generic_buffer * buffer)
254 {
255   INIT_HOST_HANDLER (host_handler, dev);
256
257   return host_handler->allocGenericBuffer (buffer);
258 }
259
260 /**
261  * @brief Free the generic buffer and remove the address mapping
262  * @param[in] dev The NPU device handle
263  * @param[in] buffer the model buffer
264  * @return 0 if no error, otherwise a negative errno.
265  */
266 int cleanNPU_genericBuffer (npudev_h dev, generic_buffer * buffer)
267 {
268   INIT_HOST_HANDLER (host_handler, dev);
269
270   return host_handler->deallocGenericBuffer (buffer);
271 }
272
273 /**
274  * @brief Allocate generic buffers, which have multiple instances of generic_buffer
275  * @param[in] dev The NPU device handle
276  * @param[in/out] buffers generic buffers.
277  * @return 0 if no error, otherwise a negative errno.
278  * @note it reuses allocGenericBuffer().
279  */
280 int allocNPU_genericBuffers (npudev_h dev, generic_buffers * buffers)
281 {
282   INIT_HOST_HANDLER (host_handler, dev);
283
284   return host_handler->allocGenericBuffer (buffers);
285 }
286
287 /**
288  * @brief Free generic buffers allocated by allocGenericBuffers().
289  * @param[in] dev The NPU device handle
290  * @param[in/out] buffers generic buffers.
291  * @note it reuses cleanGenericbuffer().
292  * @return 0 if no error, otherwise a negative errno.
293  */
294 int cleanNPU_genericBuffers (npudev_h dev, generic_buffers * buffers)
295 {
296   INIT_HOST_HANDLER (host_handler, dev);
297
298   return host_handler->deallocGenericBuffer (buffers);
299 }
300
301 /**
302  * @brief alias of allocNPU_genericBuffer for model buffer
303  */
304 int allocNPU_modelBuffer (npudev_h dev, generic_buffer * model)
305 {
306   return allocNPU_genericBuffer (dev, model);
307 }
308
309 /**
310  * @brief alias of cleanNPU_genericBuffer for model buffer
311  */
312 int cleanNPU_modelBuffer (npudev_h dev, generic_buffer * model)
313 {
314   return cleanNPU_genericBuffer (dev, model);
315 }
316
317 /**
318  * @brief alias of allocNPU_genericBuffer for input buffer
319  */
320 int allocNPU_inputBuffer (npudev_h dev, generic_buffer * input)
321 {
322   return allocNPU_genericBuffer (dev, input);
323 }
324
325 /**
326  * @brief alias of cleanNPU_genericBuffer for input buffer
327  */
328 int cleanNPU_inputBuffer (npudev_h dev, generic_buffer * input)
329 {
330   return cleanNPU_genericBuffer (dev, input);
331 }
332
333 /**
334  * @brief alias of allocNPU_genericBuffers for input buffers
335  */
336 int allocNPU_inputBuffers (npudev_h dev, input_buffers * input)
337 {
338   return allocNPU_genericBuffers (dev, input);
339 }
340
341 /**
342  * @brief alias of cleanNPU_genericBuffers for input buffers
343  */
344 int cleanNPU_inputBuffers (npudev_h dev, input_buffers * input)
345 {
346   return cleanNPU_genericBuffers (dev, input);
347 }
348
349 /**
350  * @brief get the current memory status for the given device
351  * @param[in] dev The NPU device handle
352  * @param[out] alloc_total The size of allocated memory until now
353  * @param[out] free_total The size of freed memory until now
354  * @return @c 0 if no error. otherwise a negatice error value
355  */
356 int getNPU_memoryStatus(npudev_h dev, size_t *alloc_total, size_t *free_total)
357 {
358   INIT_HOST_HANDLER (host_handler, dev);
359
360   return host_handler->getMemoryStatus (alloc_total, free_total);
361 }
362
363 /**
364  * @brief Get the current device status to be used
365  * @param[in] dev The NPU device handle
366  * @param[out] status the device status
367  * @param[out] num_requests the number of running requests (or pending)
368  * @return 0 if no error, otherwise a negative errno.
369  */
370 int getNPU_deviceStatus(npudev_h dev, npu_status *status, uint32_t *num_requests)
371 {
372   INIT_HOST_HANDLER (host_handler, dev);
373
374   return host_handler->getDeviceStatus (status, num_requests);
375 }
376
377 /**
378  * @brief Get metadata for NPU model
379  * @param[in] model The path of model binary file
380  * @param[in] need_extra whether you want to extract the extra data in metadata
381  * @return the metadata structure to be filled if no error, otherwise nullptr
382  *
383  * @note For most npu-engine users, the extra data is not useful because it will be
384  *       used for second-party users (e.g., compiler, simulator).
385  *       Also, the caller needs to free the metadata.
386  *
387  * @note the caller needs to free the metadata
388  */
389 npubin_meta * getNPUmodel_metadata (const char *model, bool need_extra)
390 {
391   npubin_meta *meta;
392   FILE *fp;
393   size_t ret;
394
395   if (!model)
396     return nullptr;
397
398   fp = fopen (model, "rb");
399   if (!fp) {
400     logerr (TAG, "Failed to open the model binary: %d\n", -errno);
401     return nullptr;
402   }
403
404   meta = (npubin_meta *) malloc (NPUBIN_META_SIZE);
405   if (!meta) {
406     logerr (TAG, "Failed to allocate metadata\n");
407     goto exit_err;
408   }
409
410   ret = fread (meta, 1, NPUBIN_META_SIZE, fp);
411   if (ret != NPUBIN_META_SIZE) {
412     logerr (TAG, "Failed to read the metadata\n");
413     goto exit_free;
414   }
415
416   if (!CHECK_NPUBIN (meta->magiccode)) {
417     logerr (TAG, "Invalid metadata provided\n");
418     goto exit_free;
419   }
420
421   if (need_extra && NPUBIN_META_EXTRA (meta->magiccode) > 0) {
422     npubin_meta *new_meta;
423
424     new_meta = (npubin_meta *) malloc (NPUBIN_META_TOTAL_SIZE(meta->magiccode));
425     if (!new_meta) {
426       logerr (TAG, "Failed to allocate extra metadata\n");
427       goto exit_free;
428     }
429
430     memcpy (new_meta, meta, NPUBIN_META_TOTAL_SIZE(meta->magiccode));
431     free (meta);
432     meta = nullptr;
433     ret = fread (new_meta->reserved_extra, 1, NPUBIN_META_EXTRA_SIZE (new_meta->magiccode), fp);
434     if (ret != NPUBIN_META_EXTRA_SIZE (new_meta->magiccode)) {
435       logerr (TAG, "Invalid extra metadata provided\n");
436       free (new_meta);
437       goto exit_err;
438     }
439
440     meta = new_meta;
441   }
442
443   fclose (fp);
444
445   return meta;
446
447 exit_free:
448   free (meta);
449 exit_err:
450   fclose (fp);
451
452   return nullptr;
453 }
454
455 /** implement methods of HostHandler class */
456
457 /** @brief host handler constructor */
458 HostHandler::HostHandler (Device *device)
459   : device_(device),
460     /* ignored as we don't use double buffering anymore, but for backward-compatibility */
461     async_mode_ (NPUASYNC_WAIT)
462 {
463 }
464
465 /** @brief host handler destructor */
466 HostHandler::~HostHandler ()
467 {
468 }
469
470 /**
471  * @brief register model from generic buffer
472  * @param[in] model_buf model buffer
473  * @param[out] modelid model id
474  * @return 0 if no error. otherwise a negative errno
475  */
476 int
477 HostHandler::registerModel (generic_buffer *model_buf, uint32_t *modelid)
478 {
479   if (model_buf == nullptr || modelid == nullptr) {
480     logerr (TAG, "Invalid arguments given\n");
481     return -EINVAL;
482   }
483
484   Model *model = nullptr;
485   int status = device_->setModel (model_buf, &model);
486   if (status != 0) {
487     logerr (TAG, "Failed to set model: %d\n", status);
488     return status;
489   }
490
491   assert (model != nullptr);
492
493   status = models_.insert (model->getID(), model);
494   if (status != 0) {
495     logerr (TAG, "Failed to insert model id\n");
496     delete model;
497     return status;
498   }
499
500   *modelid = model->getID();
501   return 0;
502 }
503
504 /**
505  * @brief remove the registered model
506  * @param[in] modelid model id
507  * @return 0 if no error. otherwise a negative errno
508  */
509 int
510 HostHandler::unregisterModel (uint32_t modelid)
511 {
512   Model *model = models_.find (modelid);
513   if (model == nullptr)
514     return -ENOENT;
515
516   int status = device_->unsetModel (model);
517   if (status != 0) {
518     logerr (TAG, "Failed to unset model: %d\n", status);
519     return status;
520   }
521
522   return models_.remove (modelid);
523 }
524
525 /**
526  * @brief remove all registered models
527  * @return 0
528  */
529 int
530 HostHandler::unregisterModels ()
531 {
532   std::function <bool (Model *)> functor =
533     [&] (Model *m) -> bool {
534       bool can_remove = true;
535       int status = device_->unsetModel (m);
536       if (status != 0) {
537         logwarn (TAG, "Failed to unset model: %d\n", status);
538         can_remove = false;
539       }
540       return can_remove;
541     };
542
543   models_.for_each (functor);
544   return 0;
545 }
546
547 /**
548  * @brief Get the profile information from NPU
549  * @param[in] run_id The identifier for each inference
550  * @param[out] profile The profile instance
551  * @return 0 if no error, otherwise a negative errno.
552  */
553 int
554 HostHandler::getProfile (int run_id, npu_profile *profile)
555 {
556   if (run_id < 0 || profile == nullptr) {
557     logerr (TAG, "Invalid parameter provided\n");
558     return -EINVAL;
559   }
560
561   const DriverAPI * api = device_->getDriverAPI ();
562   assert (api != nullptr);
563
564   void *profile_buffer;
565   int status = api->getProfile (run_id, &profile_buffer);
566   if (status != 0) {
567     // TODO: Perform parsing
568   }
569
570   return status;
571 }
572
573 /**
574  * @brief Set the data layout for input/output tensors
575  * @param[in] modelid The ID of model whose layouts are set
576  * @param[in] in the layout/type info for input tensors
577  * @param[in] out the layout/type info for output tensors
578  * @return @c 0 if no error. otherwise a negative error value
579  * @note if this function is not called, default layout/type will be used.
580  */
581 int
582 HostHandler::setDataInfo (uint32_t modelid, tensors_data_info *in,
583     tensors_data_info *out)
584 {
585   Model *model = models_.find (modelid);
586   if (model == nullptr)
587     return -ENOENT;
588
589   return model->setDataInfo (in, out);
590 }
591
592 /**
593  * @brief Set the inference constraint for next NPU inferences
594  * @param[in] modelid The target model id
595  * @param[in] constraint inference constraint (e.g., timeout, priority)
596  * @return @c 0 if no error. otherwise a negative error value
597  * @note If this function is not called, default values are used.
598  */
599 int
600 HostHandler::setConstraint (uint32_t modelid, npuConstraint constraint)
601 {
602   Model *model = models_.find (modelid);
603   if (model == nullptr)
604     return -ENOENT;
605
606   model->setConstraint (constraint);
607
608   return 0;
609 }
610
611 /**
612  * @brief find and return model instance
613  * @param[in] modelid model id
614  * @return model instance if found. otherwise nullptr
615  */
616 Model *
617 HostHandler::getModel (uint32_t modelid)
618 {
619   return models_.find (modelid);
620 }
621
622 /** @brief dummay callback for runSync. */
623 class callbackSync {
624   public:
625     callbackSync (output_buffers *output) : output_(output), done_(false) {}
626
627     static void callback (output_buffers *output, uint64_t sequence, void *data) {
628       callbackSync *sync = static_cast<callbackSync *>(data);
629       sync->callback (output, sequence);
630     }
631
632     void callback (output_buffers *output, uint64_t sequence) {
633       if (output_ != nullptr) {
634         /** just copy internal variables of output buffers */
635         memcpy (output_, output, sizeof (output_buffers));
636       }
637       done_ = true;
638       cv_.notify_one ();
639     }
640
641     void wait () {
642       std::unique_lock<std::mutex> lock (m_);
643       cv_.wait (lock, [this]() { return done_; });
644     }
645
646   private:
647     std::mutex m_;
648     std::condition_variable cv_;
649     output_buffers *output_;
650     bool done_;
651 };
652
653 /**
654  * @brief Execute inference. Wait (block) until the output is available.
655  * @param[in] modelid The model to be inferred.
656  * @param[in] input The input data to be inferred.
657  * @param[out] output The output result.
658  * @return @c 0 if no error. otherwise a negative error value
659  */
660 int
661 HostHandler::runSync (uint32_t modelid, const input_buffers *input,
662     output_buffers *output)
663 {
664   callbackSync sync (output);
665   int status = runAsync (modelid, input, callbackSync::callback,
666       static_cast <void*> (&sync), NPUASYNC_DROP_OLD, nullptr);
667   if (status == 0) {
668     /** sync needs to wait callback */
669     sync.wait ();
670   }
671   return status;
672 }
673
674 /**
675  * @brief Invoke NPU inference. Unblocking call.
676  * @param[in] modelid The model to be inferred.
677  * @param[in] input The input data to be inferred.
678  * @param[in] cb The output buffer handler.
679  * @param[in] cb_data The data given as a parameter to the runNPU_async call.
680  * @param[in] mode Configures how this operation works.
681  * @param[out] sequence The sequence number returned with runNPU_async.
682  * @return @c 0 if no error. otherwise a negative error value
683  */
684 int
685 HostHandler::runAsync (uint32_t modelid, const input_buffers *input,
686     npuOutputNotify cb, void *cb_data, npu_async_mode mode, uint64_t *sequence)
687 {
688   Model *model = nullptr;
689
690   if (device_->needModel()) {
691     model = getModel (modelid);
692     if (model == nullptr)
693       return -ENOENT;
694   }
695
696   /* check the given model before running */
697   if (model != nullptr && !model->finalize ()) {
698     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
699     return -EINVAL;
700   }
701
702   device_->setAsyncMode (mode);
703   return device_->run (NPUINPUT_HOST, model, input, cb, cb_data, sequence);
704 }
705
706 /**
707  * @brief Let NPU accept input frames from its internal source continuously
708  * @param[in] modelid The model to be inferred.
709  * @param[in] opmode NPU has different opmode with auto-inputs. Choose one.
710  * @param[in] hw_dev The target device feeding input data
711  * @return @c 0 if no error. otherwise a negative error value
712  */
713 int
714 HostHandler::runInternal (uint32_t modelid, npu_input_opmode opmode,
715     std::string hw_dev)
716 {
717   Model *model = nullptr;
718
719   if (device_->needModel()) {
720     model = getModel (modelid);
721     if (model == nullptr)
722       return -ENOENT;
723   }
724
725   /* check the given model before running */
726   if (model != nullptr && !model->finalize ()) {
727     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
728     return -EINVAL;
729   }
730
731   return device_->runInternal (opmode, model, hw_dev);
732 }
733
734 /**
735  * @brief Stop the request with the given id
736  * @param[in] dev The NPU device handle
737  * @param[in] id The request id
738  * @return @c 0 if no error. otherwise a negative error value
739  */
740 int
741 HostHandler::stopInternal (int id)
742 {
743   if (id <= 0) {
744     logerr (TAG, "Unable to stop this request with id (%d)\n", id);
745     return -EINVAL;
746   }
747
748   const DriverAPI * api = device_->getDriverAPI ();
749   assert (api != nullptr);
750
751   return api->stop_target (id);
752 }
753
754 /**
755  * @brief get number of available devices
756  * @param[in] type device type
757  * @return number of devices
758  */
759 int
760 HostHandler::getNumDevices (dev_type type)
761 {
762   return DriverAPI::getNumDevices (type);
763 }
764
765 /**
766  * @brief get device instance
767  * @param[out] dev device instance
768  * @param[in] type device type
769  * @param[in] id device id
770  * @return 0 if no error. otherwise a negative errno
771  */
772 int
773 HostHandler::getDevice (npudev_h *dev, dev_type type, uint32_t id)
774 {
775   int num_devices = getNumDevices (type);
776
777   /** check the validity of device id */
778   if (!(num_devices > 0 && id < static_cast<uint32_t>(num_devices))) {
779     logerr (TAG, "Invalid arguments provided\n");
780     return -ENODEV;
781   }
782
783   Device *device = Device::createInstance (type, id);
784   if (device == nullptr) {
785     logerr (TAG, "Failed to create a device with the given type\n");
786     return -EINVAL;
787   }
788
789   *dev = device;
790   /** This is just for backward-compatility; we don't guarantee its corresness */
791   latest_dev_ = *dev;
792
793   return 0;
794 }
795
796 /**
797  * @brief allocate generic buffer (just for users)
798  * @param[out] buffer buffer instance
799  * @return 0 if no error. otherwise a negative errno
800  */
801 int
802 HostHandler::allocGenericBuffer (generic_buffer *buffer)
803 {
804   if (buffer == NULL)
805     return -EINVAL;
806
807   if (buffer->size == 0) {
808     logerr (TAG, "Invalid size\n");
809     return -EINVAL;
810   }
811
812   if (buffer->size > UINT32_MAX) {
813     logerr (TAG, "Don't support such a large size");
814     return -ENOMEM;
815   }
816
817   switch (buffer->type) {
818     case BUFFER_FILE:
819       /* nothing to do */
820       if (buffer->filepath == nullptr)
821         return -EINVAL;
822       break;
823     case BUFFER_MAPPED:
824     {
825       /* now, npu-engine always provides dmabuf-based allocation */
826       void *addr = nullptr;
827       int dmabuf = device_->allocMemory (buffer->size, &addr);
828       if (dmabuf < 0)
829         return dmabuf;
830
831       buffer->dmabuf = dmabuf;
832       buffer->offset = 0;
833       buffer->addr = addr;
834     } break;
835     default:
836       return -EINVAL;
837   }
838
839   return 0;
840 }
841
842 /**
843  * @brief deallocate generic buffer (just for users)
844  * @param[in] buffer buffer instance
845  * @return 0 if no error. otherwise a negative errno
846  */
847 int
848 HostHandler::deallocGenericBuffer (generic_buffer *buffer)
849 {
850   if (buffer == NULL)
851     return -EINVAL;
852
853   switch (buffer->type) {
854     case BUFFER_FILE:
855       /** always true cuz nothing to do */
856       break;
857     case BUFFER_MAPPED:
858       return device_->deallocMemory (buffer->dmabuf, buffer->size, buffer->addr);
859     default:
860       return -EINVAL;
861   }
862
863   return 0;
864 }
865
866 /**
867  * @brief allocate multiple generic buffers (just for users)
868  * @param[out] buffers multi-buffer instance
869  * @return 0 if no error. otherwise a negative errno
870  */
871 int
872 HostHandler::allocGenericBuffer (generic_buffers *buffers)
873 {
874   uint32_t idx;
875   int status = 0;
876
877   if (buffers == NULL || buffers->num_buffers < 1)
878     return -EINVAL;
879
880   for (idx = 0; idx < buffers->num_buffers; idx++) {
881     status = allocGenericBuffer (&buffers->bufs[idx]);
882     if (status != 0)
883       goto free_buffer;
884   }
885
886   return 0;
887
888 free_buffer:
889   while (idx != 0) {
890     deallocGenericBuffer (&buffers->bufs[--idx]);
891   }
892
893   return status;
894 }
895
896 /**
897  * @brief deallocate multiple generic buffers (just for users)
898  * @param[in] buffers multi-buffer instance
899  * @return 0 if no error. otherwise a negative errno
900  */
901 int
902 HostHandler::deallocGenericBuffer (generic_buffers *buffers)
903 {
904   if (buffers == NULL || buffers->num_buffers < 1)
905     return -EINVAL;
906
907   for (uint32_t idx = 0; idx < buffers->num_buffers; idx++)
908     deallocGenericBuffer (&buffers->bufs[idx]);
909   buffers->num_buffers = 0;
910
911   return 0;
912 }
913
914 /**
915  * @brief get the current memory status
916  * @param[out] alloc_total The size of allocated memory until now
917  * @param[out] free_total The size of freed memory until now
918  * @return 0 if no error. otherwise a negatice error value
919  */
920 int
921 HostHandler::getMemoryStatus (size_t *alloc_total, size_t *free_total)
922 {
923   /** API is always set in initialize () */
924   const DriverAPI * api = device_->getDriverAPI ();
925   assert (api != nullptr);
926
927   return api->getMemoryStatus (alloc_total, free_total);
928 }
929
930 /**
931  * @brief Get the current device status to be used
932  * @param[out] status the device status
933  * @param[out] num_requests the number of running requests (or pending)
934  * @return 0 if no error, otherwise a negative errno.
935  */
936 int
937 HostHandler::getDeviceStatus (npu_status *status, uint32_t *num_requests)
938 {
939   /** API is always set in initialize () */
940   const DriverAPI * api = device_->getDriverAPI ();
941
942   if (!api)
943     return -EINVAL;
944
945   device_state_t state = api->isReady ();
946   if (state == device_state_t::STATE_READY) {
947     *num_requests = api->numRequests ();
948     if (*num_requests > 0)
949       *status = NPU_READY;
950     else
951       *status = NPU_IDLE;
952   } else {
953     *num_requests = 0;
954     *status = NPU_ERROR;
955   }
956
957   return 0;
958 }
959
960 /** implement methods of Device class */
961
962 /** @brief constructor of device */
963 Device::Device (dev_type type, int id, bool need_model)
964   : comm_ (CommPlugin::getCommPlugin()), type_ (type), id_ (id), need_model_ (true),
965     mode_ (NPUASYNC_WAIT), initialized_ (false), atomic_flag_ (ATOMIC_FLAG_INIT)
966 {
967 }
968
969 /**
970  * @brief create device instance depending on device type and id
971  * @param[in] type device type
972  * @param[in] id device id
973  * @return device instance
974  */
975 Device *
976 Device::createInstance (dev_type type, int id)
977 {
978   Device *device = nullptr;
979
980   switch (type & DEVICETYPE_MASK) {
981     case DEVICETYPE_TRIV:
982       device = new TrinityVision (id);
983       break;
984     case DEVICETYPE_TRIV2:
985       device = new TrinityVision2 (id);
986       break;
987     case DEVICETYPE_TRIA:
988       device = new TrinityAsr (id);
989       device->setNeedModel (false);
990       break;
991     default:
992       break;
993   }
994
995   if (device != nullptr && device->init () != 0) {
996     delete device;
997     device = nullptr;
998   }
999
1000   return device;
1001 }
1002
1003 /**
1004  * @brief device initialization
1005  * @return 0 if no error, otherwise a negative errno
1006  * @note Init failures come from createDriverAPI() only.
1007  */
1008 int
1009 Device::init ()
1010 {
1011   /** should be initilizaed only once */
1012   if (!atomic_flag_.test_and_set()) {
1013     /** create the corresponding driver API */
1014     api_ = DriverAPI::createDriverAPI (type_, id_);
1015     if (api_.get() == nullptr) {
1016       atomic_flag_.clear();
1017       logerr (TAG, "Failed to create driver API\n");
1018       return -EINVAL;
1019     }
1020
1021     handler_.reset (new HostHandler (this));
1022     scheduler_.reset (new Scheduler (api_.get()));
1023     mem_ = MemAllocator::createInstance (api_.get());
1024
1025     initialized_ = true;  /** c++11 does not provide test() of atomic flag */
1026   }
1027
1028   return 0;
1029 }
1030
1031 /**
1032  * @brief stop all requests from this device
1033  * @param[in] force_stop indicate the schedduler waits until to handle previous requests
1034  * @return 0 if no error, otherwise a negative errno
1035  */
1036 int
1037 Device::stop (bool force_stop)
1038 {
1039   if (!initialized ()) {
1040     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1041     return -EPERM;
1042   }
1043
1044   Request *req = new Request (NPUINPUT_STOP);
1045   req->setForceStop (force_stop);
1046   return scheduler_->submitRequest (req);
1047 }
1048
1049 /**
1050  * @brief allocate generic memory buffer
1051  * @param[in] size the size to allocate
1052  * @param[out] addr the mapped address
1053  * @return dmabuf fd if no error, otherwise a negative errno
1054  */
1055 int
1056 Device::allocMemory (size_t size, void **addr)
1057 {
1058   if (!initialized ()) {
1059     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1060     return -EPERM;
1061   }
1062
1063   if (size == 0 || addr == nullptr) {
1064     logerr (TAG, "Invalid arguments\n");
1065     return -EINVAL;
1066   }
1067
1068   return mem_->allocMemory (size, addr);
1069 }
1070
1071 /**
1072  * @brief deallocate generic memory buffer
1073  * @param[in] dmabuf_fd dmabuf file descriptor
1074  * @param[in] size buffer size
1075  * @param[in] addr mapped addr
1076  * @return 0 if no error, otherwise a negative errno
1077  */
1078 int
1079 Device::deallocMemory (int dmabuf_fd, size_t size, void * addr)
1080 {
1081   if (!initialized ()) {
1082     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1083     return -EPERM;
1084   }
1085
1086   if (dmabuf_fd < 0 || size == 0 || addr == nullptr) {
1087     logerr (TAG, "Invalid arguments\n");
1088     return -EINVAL;
1089   }
1090
1091   return mem_->deallocMemory (dmabuf_fd, size, addr);
1092 }
1093
1094 /**
1095  * @brief extract the buffer instance from input generic buffers
1096  * @param[in] meta the model metadata
1097  * @param[in] input the input generic buffers
1098  * @return the buffer instance
1099  */
1100 Buffer *
1101 TrinityVision::prepareInputBuffers (const Metadata *meta, const input_buffers *input)
1102 {
1103   if (meta == nullptr || input == nullptr ||
1104       meta->getInputNum() != input->num_buffers) {
1105     logerr (TAG, "Invalid metadata info provided\n");
1106     return nullptr;
1107   }
1108
1109   Buffer * buffer;
1110   const generic_buffer *first = &input->bufs[0];
1111   if (first->type == BUFFER_DMABUF) {
1112     buffer = mem_->allocBuffer (new HWmemExternal);
1113     if (buffer == nullptr)
1114       return nullptr;
1115
1116     buffer->setDmabuf (first->dmabuf);
1117     buffer->setOffset (first->offset);
1118     buffer->setSize (meta->getBufferSize());
1119   } else {
1120     buffer = mem_->allocBuffer (new HWmemDevice);
1121     if (buffer == nullptr)
1122       return nullptr;
1123
1124     int status = buffer->alloc (meta->getBufferSize ());
1125     if (status != 0) {
1126       logerr (TAG, "Failed to allocate buffer: %d\n", status);
1127       delete buffer;
1128       return nullptr;
1129     }
1130   }
1131
1132   int status = buffer->createTensors (meta);
1133   if (status != 0) {
1134     logerr (TAG, "Failed to create tensors: %d\n", status);
1135     delete buffer;
1136     buffer = nullptr;
1137   }
1138
1139   return buffer;
1140 }
1141
1142 /**
1143  * @brief implementation of TRIV's setModel ()
1144  * @param[in] model_buf the model generic buffer
1145  * @param[out] model the model instance
1146  * @return 0 if no error, otherwise a negative errno
1147  */
1148 int
1149 TrinityVision::setModel (const generic_buffer *model_buf, Model ** model_ptr)
1150 {
1151   if (!initialized ()) {
1152     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1153     return -EPERM;
1154   }
1155
1156   if (model_buf == nullptr || model_ptr == nullptr)
1157     return -EINVAL;
1158
1159   Model *model = nullptr;
1160   HWmem * hwmem_prog = nullptr;
1161   HWmem * hwmem_weight = nullptr;
1162   int status;
1163
1164   /** In TRIV1, model data (including program/weight) should be contiguous */
1165
1166   switch (model_buf->type) {
1167   case BUFFER_FILE:
1168   case BUFFER_MAPPED:
1169     model = mem_->allocModel (new HWmemDevice);
1170     if (model == nullptr) {
1171       logerr (TAG, "Failed to allocate model\n");
1172       return -ENOMEM;
1173     }
1174
1175     status = model->alloc (model_buf->size);
1176     if (status != 0) {
1177       logerr (TAG, "Failed to allocate model: %d\n", status);
1178       goto delete_exit;
1179     }
1180
1181     /** extract the whole model data */
1182     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr);
1183     if (status != 0) {
1184       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1185       goto delete_exit;
1186     }
1187     break;
1188   default:
1189     return -EINVAL;
1190   }
1191
1192   status = model->setMetadata (model->getData());
1193   if (status != 0)
1194     goto delete_exit;
1195
1196   /** allocate program (optional; NOP) */
1197   if (model->getMetadata()->getProgramSize() > 0) {
1198     hwmem_prog = new HWmem (new HWmemChunk);
1199     model->setProgramData (hwmem_prog);
1200
1201     hwmem_prog->setParent (model);
1202     hwmem_prog->setOffset (model->getMetadata()->getMetaSize());
1203     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
1204     if (status != 0) {
1205       logerr (TAG, "Failed to allocate program\n");
1206       goto delete_exit;
1207     }
1208   }
1209
1210   /** allocate weight (optional) */
1211   if (model->getMetadata()->getWeightSize() > 0) {
1212     hwmem_weight = new HWmem (new HWmemChunk);
1213     model->setWeightData (hwmem_weight);
1214
1215     hwmem_weight->setParent (model);
1216     hwmem_weight->setOffset (model->getMetadata()->getMetaSize() +
1217         model->getMetadata()->getProgramSize());
1218     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
1219     if (status != 0) {
1220       logerr (TAG, "Failed to allocate program\n");
1221       goto delete_exit;
1222     }
1223   }
1224
1225   if (hwmem_prog != nullptr) {
1226     /** register this model to the driver */
1227     model_config_t config;
1228     config.dbuf_fd = hwmem_prog->getDmabuf ();
1229     config.program_size = hwmem_prog->getSize ();
1230     config.program_offset_addr = hwmem_prog->getOffset ();
1231     if (hwmem_weight != nullptr)
1232       config.weight_offset_addr = hwmem_weight->getOffset ();
1233
1234     status = api_->registerModel (&config);
1235     if (status != 0)
1236       goto delete_exit;
1237
1238     model->setInternalID(config.id);
1239   }
1240
1241   *model_ptr = model;
1242   return status;
1243
1244 delete_exit:
1245   delete model;
1246   return status;
1247 }
1248
1249 /**
1250  * @brief implementation of TRIV's unsetModel ()
1251  * @param[in] model the model instance
1252  * @return 0 if no error, otherwise a negative errno
1253  */
1254 int
1255 TrinityVision::unsetModel (Model * model)
1256 {
1257   if (!initialized ()) {
1258     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1259     return -EPERM;
1260   }
1261
1262   if (model == nullptr) {
1263     logerr (TAG, "Invalid model instance\n");
1264     return -EINVAL;
1265   }
1266
1267   if (model->getMetadata()->getProgramSize() > 0)
1268     return api_->deregisterModel (model->getInternalID ());
1269
1270   return 0;
1271 }
1272
1273 /**
1274  * @brief implementation of TRIV's run()
1275  * @param[in] opmode input opmode
1276  * @param[in] model the model instance
1277  * @param[in] input generic buffers of input data
1278  * @param[in] cb the output callback
1279  * @param[in] cb_data the output callback data
1280  * @param[out] sequence The sequence number returned with runNPU_async.
1281  */
1282 int
1283 TrinityVision::run (npu_input_opmode opmode, const Model *model,
1284     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1285     uint64_t *sequence)
1286 {
1287   if (!initialized ()) {
1288     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1289     return -EPERM;
1290   }
1291
1292   if (opmode != NPUINPUT_HOST) {
1293     logerr (TAG, "TRIV supports only host inputservice\n");
1294     return -EINVAL;
1295   }
1296
1297   if (model == nullptr || input == nullptr) {
1298     logerr (TAG, "TRIV requires both model and input buffers\n");
1299     return -EINVAL;
1300   }
1301
1302   const_cast<Model *>(model)->updateDataInfo ();
1303
1304   Buffer *buffer = prepareInputBuffers (model->getMetadata(), input);
1305   if (buffer == nullptr) {
1306     logerr (TAG, "Failed to extract buffer instance\n");
1307     return -EINVAL;
1308   }
1309
1310   if (!buffer->isExternal ()) {
1311     for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
1312       auto func = std::bind (TrinityVision::manipulateData, model, idx, true,
1313           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1314       int status = comm_.extractGenericBuffer (&input->bufs[idx],
1315           buffer->getInputTensor(idx)->getData(), func);
1316       if (status != 0) {
1317         logerr (TAG, "Failed to feed input buffer: %d\n", status);
1318         return status;
1319       }
1320     }
1321   }
1322
1323   /** this device uses CMA buffer */
1324
1325   Request *req = new Request (opmode);
1326   req->setModel (model);
1327   req->setBuffer (buffer);
1328
1329   if (cb != nullptr)
1330     req->setCallback (std::bind (&TrinityVision::callback, this, req, cb, cb_data));
1331
1332   if (sequence != nullptr)
1333     *sequence = req->getID();
1334
1335   return scheduler_->submitRequest (req);
1336 }
1337
1338 /**
1339  * @brief callback of TRIV2 request
1340  * @param[in] req the request instance
1341  * @param[in] cb callback for completion
1342  * @param[in] cb_data callback data
1343  * @note The callback invoke does not gurantee the request was successful
1344  * @todo Check the request failures
1345  */
1346 void
1347 TrinityVision::callback (Request *req, npuOutputNotify cb, void *cb_data)
1348 {
1349   const Model *model = req->getModel ();
1350   Buffer *buffer = req->getBuffer ();
1351   output_buffers output = {
1352     .num_buffers = buffer->getOutputNum ()
1353   };
1354
1355   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
1356     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
1357
1358     if (buffer->isExternal ()) {
1359       output.bufs[idx].type = BUFFER_DMABUF;
1360       output.bufs[idx].size = output_tensor_size;
1361       output.bufs[idx].addr = buffer->getOutputTensor(idx)->getData();
1362     } else {
1363       output.bufs[idx].type = BUFFER_MAPPED;
1364       output.bufs[idx].size = output_tensor_size;
1365       /** user needs to free this */
1366       output.bufs[idx].addr = malloc (output_tensor_size);
1367
1368       auto func = std::bind (TrinityVision::manipulateData, model, idx, false,
1369           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1370       int status = comm_.insertGenericBuffer (buffer->getOutputTensor(idx)->getData(),
1371           &output.bufs[idx], func);
1372       if (status != 0) {
1373         logerr (TAG, "Failed to return output buffer: %d\n", status);
1374       }
1375     }
1376   }
1377
1378   cb (&output, req->getID(), cb_data);
1379
1380   delete buffer;
1381 }
1382
1383 /**
1384  * @brief extract the segment table instance from input generic buffers
1385  * @param[in] model the model instance
1386  * @param[in] input the input generic buffers
1387  * @param[in] output the output generic buffers
1388  * @return the segment table instance
1389  */
1390 SegmentTable *
1391 TrinityVision2::prepareSegmentTable (const Model *model, const input_buffers *input,
1392     const output_buffers *output)
1393 {
1394   const Metadata *meta = model->getMetadata ();
1395   if (meta == nullptr || (input != nullptr &&
1396         meta->getInputNum() != input->num_buffers)) {
1397     logerr (TAG, "Invalid metadata info provided\n");
1398     return nullptr;
1399   }
1400
1401   SegmentTable * segt = mem_->allocSegmentTable (new HWmemDevice);
1402   int status = segt->alloc ();
1403   if (status != 0) {
1404     logerr (TAG, "Failed to allocate segment table: %d\n", status);
1405     goto delete_segt;
1406   }
1407
1408   status = segt->createSegments (model, input, output);
1409   if (status != 0) {
1410     logerr (TAG, "Failed to create segments: %d\n", status);
1411     goto delete_segt;
1412   }
1413
1414   return segt;
1415
1416 delete_segt:
1417   delete segt;
1418   return nullptr;
1419 }
1420
1421 /**
1422  * @brief implementation of TRIV2's setModel ()
1423  * @param[in] model_buf the model generic buffer
1424  * @param[out] model the model instance
1425  * @return 0 if no error, otherwise a negative errno
1426  */
1427 int
1428 TrinityVision2::setModel (const generic_buffer *model_buf, Model ** model_ptr)
1429 {
1430   if (!initialized ()) {
1431     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1432     return -EPERM;
1433   }
1434
1435   if (model_buf == nullptr || model_ptr == nullptr)
1436     return -EINVAL;
1437
1438   Model *model;
1439   int status;
1440
1441   switch (model_buf->type) {
1442   case BUFFER_FILE:
1443   case BUFFER_MAPPED:
1444     model = mem_->allocModel (new HWmemDevice);
1445     if (model == nullptr) {
1446       logerr (TAG, "Failed to allocate model\n");
1447       return -ENOMEM;
1448     }
1449
1450     status = model->alloc (NPUBIN_META_SIZE);
1451     if (status != 0) {
1452       logerr (TAG, "Failed to allocate model: %d\n", status);
1453       goto delete_exit;
1454     }
1455
1456     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr,
1457         0, NPUBIN_META_SIZE);
1458     if (status != 0) {
1459       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1460       goto delete_exit;
1461     }
1462     break;
1463   default:
1464     return -EINVAL;
1465   }
1466
1467   status = model->setMetadata (model->getData());
1468   if (status != 0)
1469     goto delete_exit;
1470
1471   /** allocate program (optional; NOP) */
1472   if (model->getMetadata()->getProgramSize() > 0) {
1473     HWmem * hwmem_prog = new HWmem (new HWmemDevice);
1474     hwmem_prog->setDriverAPI (api_.get());
1475
1476     model->setProgramData (hwmem_prog);
1477
1478     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
1479     if (status != 0) {
1480       logerr (TAG, "Failed to allocate program\n");
1481       goto delete_exit;
1482     }
1483
1484     status = comm_.extractGenericBuffer (model_buf, hwmem_prog->getData(), nullptr,
1485         model->getMetadata()->getMetaSize(),
1486         model->getMetadata()->getProgramSize());
1487     if (status != 0) {
1488       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1489       goto delete_exit;
1490     }
1491
1492     /** register this model to the driver */
1493     model_config_t config;
1494     config.dbuf_fd = hwmem_prog->getDmabuf ();
1495     config.program_size = hwmem_prog->getSize ();
1496     config.program_offset_addr = 0;
1497
1498     status = api_->registerModel (&config);
1499     if (status != 0)
1500       goto delete_exit;
1501
1502     model->setInternalID(config.id);
1503   }
1504
1505   /** allocate weight (optional) */
1506   if (model->getMetadata()->getWeightSize() > 0) {
1507     HWmem * hwmem_weight = new HWmem (new HWmemDevice);
1508     hwmem_weight->setDriverAPI (api_.get());
1509
1510     model->setWeightData (hwmem_weight);
1511
1512     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
1513     if (status != 0) {
1514       logerr (TAG, "Failed to allocate program\n");
1515       goto delete_exit;
1516     }
1517
1518     status = comm_.extractGenericBuffer (model_buf, hwmem_weight->getData(), nullptr,
1519         model->getMetadata()->getMetaSize() + model->getMetadata()->getProgramSize(),
1520         model->getMetadata()->getWeightSize());
1521     if (status != 0) {
1522       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1523       goto delete_exit;
1524     }
1525   }
1526
1527   *model_ptr = model;
1528   return status;
1529
1530 delete_exit:
1531   delete model;
1532   return status;
1533 }
1534
1535 /**
1536  * @brief implementation of TRIV2's unsetModel ()
1537  * @param[in] model the model instance
1538  * @return 0 if no error, otherwise a negative errno
1539  */
1540 int
1541 TrinityVision2::unsetModel (Model * model)
1542 {
1543   if (!initialized ()) {
1544     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1545     return -EPERM;
1546   }
1547
1548   if (model == nullptr) {
1549     logerr (TAG, "Invalid model instance\n");
1550     return -EINVAL;
1551   }
1552
1553   if (model->getMetadata()->getProgramSize() > 0)
1554     return api_->deregisterModel (model->getInternalID ());
1555
1556   return 0;
1557 }
1558
1559 /** @brief implementation of TRIV2's run() */
1560 int
1561 TrinityVision2::run (npu_input_opmode opmode, const Model *model,
1562     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1563     uint64_t *sequence)
1564 {
1565   if (!initialized ()) {
1566     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1567     return -EPERM;
1568   }
1569
1570   if (opmode != NPUINPUT_HOST)
1571     return -EINVAL;
1572
1573   if (input == nullptr || input->num_buffers == 0 || model == nullptr)
1574     return -EINVAL;
1575
1576   const_cast<Model *>(model)->updateDataInfo ();
1577
1578   /** this device uses segment table */
1579   SegmentTable * segt = prepareSegmentTable (model, input);
1580   if (segt == nullptr) {
1581     logerr (TAG, "Failed to create segment table instance\n");
1582     return -EINVAL;
1583   }
1584
1585   /** extract input data */
1586   for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
1587     if (!segt->getInputSegment(idx)->isExternal ()) {
1588       uint32_t seg_offset = segt->getInputSegmentOffset(idx);
1589       auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
1590           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1591       int status = comm_.extractGenericBuffer (
1592           &input->bufs[idx],
1593           segt->getInputSegment(idx)->getData() + seg_offset,
1594           func);
1595       if (status != 0) {
1596         logerr (TAG, "Failed to feed input segment: %d\n", status);
1597         return status;
1598       }
1599     }
1600   }
1601
1602   Request *req = new Request (opmode);
1603   req->setModel (model);
1604   req->setSegmentTable (segt);
1605   req->setCallback (std::bind (&TrinityVision2::callback, this, req, cb, cb_data));
1606
1607   if (sequence)
1608     *sequence = req->getID();
1609
1610   return scheduler_->submitRequest (req);
1611 }
1612
1613 /** @brief implementation of TRIV2's runInternal() */
1614 int
1615 TrinityVision2::runInternal (npu_input_opmode opmode, const Model *model,
1616     std::string hw_dev)
1617 {
1618   if (!initialized ()) {
1619     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1620     return -EPERM;
1621   }
1622
1623   if (opmode != NPUINPUT_HW_RECURRING)
1624     return -EINVAL;
1625
1626   /** this device uses segment table */
1627   SegmentTable * segt = prepareSegmentTable (model, nullptr, nullptr);
1628   if (segt == nullptr) {
1629     logerr (TAG, "Failed to create segment table instance\n");
1630     return -EINVAL;
1631   }
1632
1633   Request *req = new Request (opmode);
1634   req->setModel (model);
1635   req->setSegmentTable (segt);
1636   req->setHwDevice (hw_dev);
1637
1638   return scheduler_->submitRequest (req);
1639 }
1640
1641 /** @brief callback of TRIV2 request */
1642 void
1643 TrinityVision2::callback (Request *req, npuOutputNotify cb, void *cb_data)
1644 {
1645   const Model *model = req->getModel ();
1646   SegmentTable *segt = req->getSegmentTable ();
1647   output_buffers output = {
1648     .num_buffers = segt->getNumOutputSegments ()
1649   };
1650
1651   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
1652     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
1653
1654     output.bufs[idx].type = BUFFER_MAPPED;
1655     output.bufs[idx].size = output_tensor_size;
1656     /** user needs to free this */
1657     output.bufs[idx].addr = calloc (1, output_tensor_size);
1658
1659 #if defined(ENABLE_FPGA_WORKAROUND)
1660     api_->fpga_memcpy (
1661         segt->getOutputSegment(idx)->getDmabuf(),
1662         segt->getOutputSegmentOffset(idx),
1663         output.bufs[idx].addr,
1664         output.bufs[idx].size);
1665 #else
1666     auto func = std::bind (TrinityVision2::manipulateData, model, idx, false,
1667         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1668     int status = comm_.insertGenericBuffer (
1669         segt->getOutputSegment(idx)->getData() + segt->getOutputSegmentOffset(idx),
1670         &output.bufs[idx], func);
1671
1672     if (status != 0) {
1673       logerr (TAG, "Failed to return output buffer: %d\n", status);
1674     }
1675 #endif
1676   }
1677
1678   cb (&output, req->getID(), cb_data);
1679
1680   delete segt;
1681 }
1682
1683 /** @brief implementation of TRIA's run(): WIP */
1684 int
1685 TrinityAsr::run (npu_input_opmode opmode, const Model *model,
1686     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1687     uint64_t *sequence)
1688 {
1689   if (!initialized ()) {
1690     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1691     return -EPERM;
1692   }
1693
1694   if (opmode != NPUINPUT_HOST)
1695     return -EINVAL;
1696
1697   if (input == nullptr || input->num_buffers != 1)
1698     return -EINVAL;
1699
1700   Buffer * buffer;
1701   int status;
1702   /** ASR does not require model and support only a single tensor */
1703   const generic_buffer *first_buf = &input->bufs[0];
1704   if (first_buf->type == BUFFER_DMABUF) {
1705     buffer = mem_->allocBuffer (new HWmemExternal);
1706     if (buffer == nullptr)
1707       return -ENOMEM;
1708
1709     buffer->setDmabuf (first_buf->dmabuf);
1710     buffer->setOffset (first_buf->offset);
1711     buffer->setSize (first_buf->size);
1712   } else {
1713     buffer = mem_->allocBuffer (new HWmemDevice);
1714     if (buffer == nullptr)
1715       return -ENOMEM;
1716
1717     status = buffer->alloc (first_buf->size);
1718     if (status != 0) {
1719       delete buffer;
1720       return status;
1721     }
1722   }
1723
1724   status = buffer->createTensors ();
1725   if (status != 0) {
1726     logerr (TAG, "Failed to create tensors: %d\n", status);
1727     delete buffer;
1728     return status;
1729   }
1730
1731   if (!buffer->isExternal ()) {
1732     status = comm_.extractGenericBuffer (first_buf,
1733         buffer->getInputTensor(0)->getData(), nullptr);
1734     if (status != 0)
1735       return status;
1736   }
1737
1738   Request *req = new Request (opmode);
1739   req->setBuffer (buffer);
1740   req->setCallback (std::bind (&TrinityAsr::callback, this, req, cb, cb_data));
1741
1742   if (sequence)
1743     *sequence = req->getID();
1744
1745   return scheduler_->submitRequest (req);
1746 }
1747
1748 /** @brief callback of TRIA request: WIP */
1749 void
1750 TrinityAsr::callback (Request *req, npuOutputNotify cb, void *cb_data)
1751 {
1752   Buffer *buffer = req->getBuffer ();
1753   output_buffers output = {
1754     .num_buffers = 0
1755   };
1756
1757   /** TODO: finalize this impl. when the ASR's working scenario is determined */
1758   cb (&output, req->getID(), cb_data);
1759
1760   delete buffer;
1761 }
1762
1763 /** Implement data manipulation (each device may have different impl.) */
1764
1765 #ifdef ENABLE_MANIP
1766
1767 /**
1768  * @brief perform data manipulation
1769  * @param[in] model model instance
1770  * @param[in] idx tensor index
1771  * @param[in] is_input indicate it's input manipulation
1772  * @param[out] dst destination buffer
1773  * @param[in] src source buffer (feature map)
1774  * @param[in] size size to be copied
1775  * @return size of memory copy if no error, otherwise zero
1776  *
1777  * @note the input data format should be NHWC
1778  * @detail rules for the memory address of activations in NPU HW.
1779  *         (https://code.sec.samsung.net/confluence/pages/viewpage.action?pageId=146491864)
1780  *
1781  * 1) Special case (depth == 3)
1782  * - addr(x,y,z) = addr(0,0,0) + (z) + 3 * (x + width * y)
1783  *
1784  * 2) Common case
1785  * - addr(x,y,z) = addr(0,0,0) + (z % MPA_L) + MPA_L * (x + width * (y + height * (z / MPA_L)))
1786  *
1787  * Thus, if depth is not a multiple of MPA_L (i.e., 64), zero padding is required
1788  */
1789 size_t
1790 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
1791     void *dst, void *src, size_t size)
1792 {
1793   const Metadata *meta = model->getMetadata();
1794   DataConverter converter (is_input);
1795
1796   converter.setData (src, dst, size);
1797
1798   if (is_input) {
1799     const tensor_data_info* info = model->getInputDataInfo (idx);
1800     if (info == nullptr)
1801       return 0;
1802
1803     converter.setDataLayout (info->layout, DATA_LAYOUT_SRNPU);
1804     converter.setDataType (info->type, DATA_TYPE_SRNPU);
1805     converter.setDataDims (meta->getInputDims (idx));
1806     converter.setQuantZero (meta->getInputQuantZero (idx));
1807     converter.setQuantScale (meta->getInputQuantScale (idx));
1808   } else {
1809     const tensor_data_info* info = model->getOutputDataInfo (idx);
1810     if (info == nullptr)
1811       return 0;
1812
1813     converter.setDataLayout (DATA_LAYOUT_SRNPU, info->layout);
1814     converter.setDataType (DATA_TYPE_SRNPU, info->type);
1815     converter.setDataDims (meta->getOutputDims (idx));
1816     converter.setQuantZero (meta->getOutputQuantZero (idx));
1817     converter.setQuantScale (meta->getOutputQuantScale (idx));
1818   }
1819
1820   return converter.perform ();
1821 }
1822
1823 /**
1824  * @brief perform data manipulation
1825  * @param[in] model model instance
1826  * @param[in] idx tensor index
1827  * @param[in] is_input indicate it's input manipulation
1828  * @param[out] dst destination buffer
1829  * @param[in] src source buffer (feature map)
1830  * @param[in] size size to be copied
1831  * @return size of memory copy if no error, otherwise zero
1832  *
1833  * @note the input data format should be NHWC
1834  *
1835  * @detail Feature map data in TRIV2, (x, y, z) = (width, height, depth)
1836  *
1837  *         1) Image input (depth == 1 or depth == 3)
1838  *            Addr(x,y,z) = Addr(0,0,0) + z + depth * x + ymod * y
1839  *
1840  *         2) Common cases
1841  *            Addr(x,y,z) = Addr(0,0,0) + (z % 64) + (64 * x) + ymod * y + zmod * (z / 64)
1842  */
1843 size_t
1844 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1845     void *dst, void *src, size_t size)
1846 {
1847   const Metadata *meta = model->getMetadata();
1848   DataConverter converter (is_input);
1849
1850   converter.setData (src, dst, size);
1851
1852   if (is_input) {
1853     const tensor_data_info* info = model->getInputDataInfo (idx);
1854     if (info == nullptr)
1855       return 0;
1856
1857     converter.setDataLayout (info->layout, DATA_LAYOUT_TRIV2);
1858     converter.setDataType (info->type, meta->getInputQuantType (idx));
1859     converter.setDataDims (meta->getInputDims (idx));
1860     converter.setQuantZero (meta->getInputQuantZero (idx));
1861     converter.setQuantScale (meta->getInputQuantScale (idx));
1862   } else {
1863     const tensor_data_info* info = model->getOutputDataInfo (idx);
1864     if (info == nullptr)
1865       return 0;
1866
1867     converter.setDataLayout (DATA_LAYOUT_TRIV2, info->layout);
1868     converter.setDataType (meta->getOutputQuantType (idx), info->type);
1869     converter.setDataDims (meta->getOutputDims (idx));
1870     converter.setQuantZero (meta->getOutputQuantZero (idx));
1871     converter.setQuantScale (meta->getOutputQuantScale (idx));
1872   }
1873
1874   return converter.perform ();
1875 }
1876
1877 #else
1878
1879 size_t
1880 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
1881     void *dst, void *src, size_t size)
1882 {
1883   memcpy (dst, src, size);
1884   return size;
1885 }
1886
1887 size_t
1888 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1889     void *dst, void *src, size_t size)
1890 {
1891   memcpy (dst, src, size);
1892   return size;
1893 }
1894
1895 #endif
1896
1897 /** other device types don't have data manip impl. yet */
1898
1899 size_t
1900 TrinityAsr::manipulateData (const Model *model, uint32_t idx, bool is_input,
1901     void *dst, void *src, size_t size)
1902 {
1903   memcpy (dst, src, size);
1904   return size;
1905 }