--- /dev/null
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 Parichay Kapoor <pk.kapoor@samsung.com>
+ * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
+ */
+/**
+ * @file NPUdrvAPI.c
+ * @date 26 June 2019
+ * @brief Implementation of API to access NPU
+ * @see http://suprem.sec.samsung.net/confluence/display/ODLC/Software+Stack
+ * @author Parichay Kapoor <pk.kapoor@samsung.com>
+ * @bug No known bugs except for NYI items
+ *
+ * To packagers: this is used by NPU Engine (NE).
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+
+#include <NPUdrvAPI.h>
+
+#define DEV_BASE_PATH "/dev"
+#define DEV_NAME "srnpu"
+#define MAX_NUM_DEVICE 1
+
+
+/** private object corresponding to each npu opened */
+typedef struct _npu_dev_priv
+{
+ int internal_fd; /** the actual fd for the opened device */
+ npu_input_opmode mode; /** operating for the npu */
+
+ /** npu copnfigration variables */
+ uint64_t program_offset_addr;
+ uint64_t program_size;
+ uint64_t weight_offset_addr;
+ uint64_t activation_offset_addr0;
+ uint64_t activation_offset_addr1;
+
+ /** Base physical address for the memory */
+ int dmabuf_fd;
+ int offset0;
+ int offset1;
+} npu_dev_priv;
+
+
+/** npu api private struct */
+typedef struct
+{
+ npu_dev_priv * npu_fd_table[MAX_NUM_DEVICE]; /** struct of opened devices */
+ pthread_mutex_t mutex; /** mutex for multiple devices */
+} npu_api_priv;
+
+
+/**
+ * global values maintain all the opened npus
+ * currently only 1 NPU is supported
+ * extened npu_fd_table to a table to support multiple devices
+ */
+static npu_api_priv napi_priv;
+
+/** constructor/destructor for npu api */
+void init_npu_api(void) __attribute__ ((constructor));
+void fini_npu_api(void) __attribute__ ((destructor));
+
+/**
+ * @brief find the object for the given fd
+ * @param[in] fd file descriptor for the device
+ * @return npu_dev_priv object on success, NULL on error
+ */
+static npu_dev_priv* fd_to_obj (int fd)
+{
+ return napi_priv.npu_fd_table[fd];
+}
+
+
+/**
+ * @brief add an entry to the fd table
+ * @param[in] fd abstracted file descriptor for the device
+ * @param[in] internal_fd actual non-abstracted file descriptor for the device
+ *
+ * @note actual file descriptor is referred as internal_fd, and abstracted
+ * fd is referred directly as fd
+ */
+static int fd_table_add_new_entry (int fd, int internal_fd)
+{
+ if (fd >= MAX_NUM_DEVICE) {
+ return -ENOMEM;
+ }
+
+ pthread_mutex_lock(&napi_priv.mutex);
+ /** cannot open the same device twice */
+ if (napi_priv.npu_fd_table[fd] != NULL) {
+ pthread_mutex_unlock(&napi_priv.mutex);
+ return -EBUSY;
+ }
+
+ napi_priv.npu_fd_table[fd] = (npu_dev_priv *) malloc (sizeof (npu_dev_priv));
+ napi_priv.npu_fd_table[fd]->internal_fd = internal_fd;
+
+ pthread_mutex_unlock(&napi_priv.mutex);
+ return 0;
+}
+
+/**
+ * @brief delete an entry from the fd table
+ * @param[in] fd file descriptor for the device
+ * return 0 on success, errno on failure
+ */
+static int fd_table_delete_entry (int fd)
+{
+ if (fd >= MAX_NUM_DEVICE) {
+ return -EMFILE;
+ }
+
+ pthread_mutex_lock(&napi_priv.mutex);
+ if (napi_priv.npu_fd_table[fd] == NULL) {
+ pthread_mutex_unlock(&napi_priv.mutex);
+ return -EBADF;
+ }
+
+ free (napi_priv.npu_fd_table[fd]);
+ napi_priv.npu_fd_table[fd] = NULL;
+
+ pthread_mutex_unlock(&napi_priv.mutex);
+
+ return 0;
+}
+
+/**
+ * @brief verify that addresses are 32bits
+ * @param[in] value address/offset value provided
+ * @return 0 on success, errno on failure
+ */
+static int verify_size32 (uint64_t value)
+{
+ if (value >> 32 == 0x0) {
+ return 0;
+ } else {
+ return -ERANGE;
+ }
+}
+
+/**
+ * @brief open the npu device
+ * @param[in] dev_id index of the device to be opened
+ * @return file descriptor is not error. otherwise errno
+ */
+int npu_open (unsigned int dev_id)
+{
+ int fd;
+ char name[64];
+ int status;
+ errno = 0;
+
+ snprintf(name, sizeof(name), "%s/%s%u", DEV_BASE_PATH, DEV_NAME, dev_id);
+
+ fd = open(name, O_RDWR);
+ if (fd < 0) {
+ return -errno;
+ }
+
+ status = fd_table_add_new_entry (dev_id, fd);
+ if (status < 0) {
+ close (fd);
+ return status;
+ }
+
+ return dev_id;
+}
+
+
+/**
+ * @brief close the npu device
+ * @param[in] fd file descriptor of the device
+ * @returns 0 on success, errno on error
+ */
+int npu_close (int fd)
+{
+ npu_dev_priv * npu_dev_priv_t;
+ int status;
+
+ npu_dev_priv_t = fd_to_obj (fd);
+ if (npu_dev_priv_t == NULL)
+ return -ENXIO;
+
+ close (npu_dev_priv_t->internal_fd);
+ status = fd_table_delete_entry (fd);
+ if (status < 0) {
+ return status;
+ }
+
+ return 0;
+}
+
+
+/**
+ * @brief set mode for the NPU to operate in
+ * @param[in] fd file descriptor of the device
+ * @param[in] mode mode of operation for the NPU
+ */
+void npu_set_mode (int fd, npu_input_opmode mode)
+{
+ npu_dev_priv * npu_dev_priv_t;
+
+ npu_dev_priv_t = fd_to_obj (fd);
+ npu_dev_priv_t->mode = mode;
+}
+
+
+/**
+ * @brief run the npu based on the set configuration
+ * @param[in] fd file descriptor of the device
+ * @return 0 on success, errno on error
+ * @note this function will block the calling thread till output is complete
+ */
+int npu_setup_device (int fd)
+{
+ return 0;
+}
+
+
+/**
+ * @brief get the status of the device
+ * @param[in] fd file descriptor of the device
+ * @return NULL to error, else register values filled in struct
+ */
+struct srnpu_status_arg * npu_get_status (int fd)
+{
+ return NULL;
+}
+
+
+/**
+ * @brief set the program instruction offset dram address and size
+ * @param[in] fd file descriptor of the device
+ * @param[in] program_offset_addr offset address for the instructions (NPU_PROG_BASE)
+ * @param[in] program_size size of the program instructions (NPU_PROG_SIZE)
+ * @return 0 on success, errno on error
+ */
+int npu_set_program (int fd, uint64_t program_offset_addr,
+ uint64_t program_size)
+{
+ npu_dev_priv * npu_dev_priv_t;
+
+ npu_dev_priv_t = fd_to_obj (fd);
+ if (npu_dev_priv_t == NULL)
+ return -ENXIO;
+
+ if (verify_size32(program_offset_addr) != 0 ||
+ verify_size32(program_size) != 0) {
+ return -ERANGE;
+ }
+
+ npu_dev_priv_t->program_offset_addr = program_offset_addr;
+ npu_dev_priv_t->program_size = program_size;
+ return 0;
+}
+
+
+/**
+ * @brief set the offset dram address for the model parameters
+ * @param[in] fd file descriptor of the device
+ * @param[in] weight_offset_addr offset address for storing weights (NPU_WGT_BASE)
+ * @return 0 on success, errno on error
+ */
+int npu_set_weight (int fd, uint64_t weight_offset_addr)
+{
+ npu_dev_priv * npu_dev_priv_t;
+
+ npu_dev_priv_t = fd_to_obj (fd);
+ if (npu_dev_priv_t == NULL)
+ return -ENXIO;
+
+ if (verify_size32(weight_offset_addr) != 0) {
+ return -ERANGE;
+ }
+
+ npu_dev_priv_t->weight_offset_addr = weight_offset_addr;
+ return 0;
+}
+
+
+/**
+ * @brief set the offset dram address for the activation/input
+ * @param[in] fd file descriptor of the device
+ * @param[in] activation_offset_addr0 offset address for storing weights (NPU_ACT_BASE0)
+ * @param[in] activation_offset_addr1 offset address for storing weights (NPU_ACT_BASE1)
+ * @return 0 on success, -1 on error
+ *
+ * @note The addr value act as offset from the base physical address
+ */
+int npu_set_activation (int fd, uint64_t activation_offset_addr0,
+ uint64_t activation_offset_addr1)
+{
+ npu_dev_priv * npu_dev_priv_t;
+
+ npu_dev_priv_t = fd_to_obj (fd);
+ if (npu_dev_priv_t == NULL)
+ return -ENXIO;
+
+ if (verify_size32(activation_offset_addr0) != 0 ||
+ verify_size32(activation_offset_addr1) != 0) {
+ return -ERANGE;
+ }
+
+ npu_dev_priv_t->activation_offset_addr0 = activation_offset_addr0;
+ npu_dev_priv_t->activation_offset_addr1 = activation_offset_addr1;
+ return 0;
+}
+
+
+/**
+ * @brief set the base dram address
+ * @param[in] fd file descriptor of the device
+ * @param[in] dmabuf_fd to extract the base physical address
+ * @param[in] offset1 to get start of first memory region
+ * @param[in] offset2 to get start of second memory region
+ * @return 0 on success, errno on error
+ */
+int npu_set_buffer (int fd, int dmabuf_fd, uint64_t offset0, uint64_t offset1)
+{
+ npu_dev_priv * npu_dev_priv_t;
+ struct srnpu_buffer_arg npu_buffer_t;
+ int ret;
+
+ npu_dev_priv_t = fd_to_obj (fd);
+ if (npu_dev_priv_t == NULL)
+ return -ENXIO;
+
+ if (verify_size32(offset0) != 0 || verify_size32(offset1) != 0) {
+ return -ERANGE;
+ }
+
+ /** for now, these two will be same */
+ npu_dev_priv_t->dmabuf_fd = dmabuf_fd;
+ npu_dev_priv_t->offset0 = offset0;
+ npu_dev_priv_t->offset1 = offset1;
+
+ /** TODO: send the dmabuf fd */
+
+ return 0;
+}
+
+
+/**
+ * @brief check if the device to get ready for the next computation
+ * @param[in] fd file descriptor of the device
+ * @return 0 if ready, errno if not ready or error
+ */
+int npu_check_compute_ready (int fd)
+{
+ return 0;
+}
+
+
+/**
+ * @brief check if the device to get ready for the next computation
+ * @param[in] fd file descriptor of the device
+ * @return 0 if ready, errno on error
+ * @note this function will block till npu is ready
+ */
+int npu_wait_compute_ready (int fd)
+{
+ return 0;
+}
+
+void init_npu_api(void)
+{
+ int i;
+
+ pthread_mutex_init(&napi_priv.mutex, NULL);
+ for (i=0; i<MAX_NUM_DEVICE; i++) {
+ napi_priv.npu_fd_table[i] = NULL;
+ }
+
+}
+
+void fini_npu_api(void)
+{
+ pthread_mutex_destroy(&napi_priv.mutex);
+}