/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_INCLUDE_RPMSG_MULTIPLE_INSTANCE_H_
#define ZEPHYR_INCLUDE_RPMSG_MULTIPLE_INSTANCE_H_

#include <openamp/open_amp.h>
#include <metal/sys.h>
#include <metal/device.h>
#include <metal/alloc.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief RPMsg multiple instance API
 * @defgroup rpmsg_multiple_instance_api RPMsg multiple instance APIs
 * @{
 */

#define VDEV_START_ADDR     CONFIG_RPMSG_MULTI_INSTANCE_SHM_BASE_ADDRESS
#define VDEV_SIZE           CONFIG_RPMSG_MULTI_INSTANCE_SHM_SIZE

#define SHM_START_ADDR      VDEV_START_ADDR
#define SHM_SIZE            VDEV_SIZE

#define VRING_ALIGNMENT     (4)     /**< Alignment of vring buffer. */
#define VDEV_STATUS_SIZE    (0x4)   /**< Size of status region. */

/** @brief Event callback structure.
 *
 *  It is registered during endpoint registration.
 *  This structure is packed into the endpoint configuration.
 */
struct rpmsg_mi_cb {
	/** @brief Bind was successful.
	 *
	 *  @param priv Private user data.
	 */
	void (*bound)(void *priv);

	/** @brief New packet arrived.
	 *
	 *  @param data Pointer to data buffer.
	 *  @param len Length of @a data.
	 *  @param priv Private user data.
	 */
	void (*received)(const void *data, size_t len, void *priv);
};

/** @brief Endpoint instance. */
struct rpmsg_mi_ept {

	/** Name of endpoint. */
	const char *name;

	/** RPMsg endpoint. */
	struct rpmsg_endpoint ep;

	/** Event callback structure. */
	struct rpmsg_mi_cb *cb;

	/** Private user data. */
	void *priv;

	/** Endpoint was bound. */
	volatile bool bound;

	/** Linked list node. */
	sys_snode_t node;
};

/** @brief Endpoint configuration. */
struct rpmsg_mi_ept_cfg {

	/** Name of endpoint. */
	const char *name;

	/** Event callback structure. */
	struct rpmsg_mi_cb *cb;

	/** Private user data. */
	void *priv;
};

/** @brief Struct describing the context of the RPMsg instance. */
struct rpmsg_mi_ctx {
	const char *name;
	struct k_work_q ipm_work_q;
	struct k_work ipm_work;

	const struct device *ipm_tx_handle;
	const struct device *ipm_rx_handle;

	unsigned int ipm_tx_id;

	uintptr_t shm_status_reg_addr;
	struct metal_io_region *shm_io;
	struct metal_device shm_device;
	metal_phys_addr_t shm_physmap[1];

	struct rpmsg_virtio_device rvdev;
	struct rpmsg_virtio_shm_pool shpool;
	struct rpmsg_device *rdev;

	struct virtqueue *vq[2];
	struct virtio_vring_info rvrings[2];
	struct virtio_device vdev;

	uintptr_t vring_tx_addr;
	uintptr_t vring_rx_addr;

	sys_slist_t endpoints;
};

struct rpmsg_mi_ctx_shm_cfg {
	/** Physical address shared memory region. */
	uintptr_t addr;

	/** Size shared memory region. */
	size_t size;

	/** Internal counter. */
	unsigned int instance;
};

/** @brief Configuration of the RPMsg instance. */
struct rpmsg_mi_ctx_cfg {

	/** Name of instance. */
	const char *name;

	/** Stack area for k_work_q. */
	k_thread_stack_t *ipm_stack_area;

	/** Size of stack area. */
	size_t ipm_stack_size;

	/** Priority of work_q. */
	int ipm_work_q_prio;

	/** Name of work_q thread. */
	const char *ipm_thread_name;

	/** Name of the TX IPM channel. */
	const char *ipm_tx_name;

	/** Name of the RX IPM channel. */
	const char *ipm_rx_name;

	/** IPM message identifier. */
	unsigned int ipm_tx_id;

	/** SHM struct. */
	struct rpmsg_mi_ctx_shm_cfg *shm;
};

/** @brief Initialization of RPMsg instance.
 *
 *  Each instance has an automatically allocated area of shared memory.
 *
 * @param ctx Pointer to the RPMsg instance.
 * @param cfg Pointer to the configuration structure.
 * @retval 0 if the operation was successful.
 *         -EINVAL when the incorrect parameters have been passed.
 *         -EIO when the configuration is not correct.
 *         -ENODEV failed to get TX or RX IPM handle.
 *         -ENOMEM when there is not enough memory to register virqueue.
 *         < 0 on other negative errno code, reported by rpmsg.
 */
int rpmsg_mi_ctx_init(struct rpmsg_mi_ctx *ctx, const struct rpmsg_mi_ctx_cfg *cfg);

/** @brief Register IPC endpoint.
 *
 *  Registers IPC endpoint to enable communication with a remote device.
 *
 *  @param ctx Pointer to the RPMsg instance.
 *  @param ept Pointer to endpoint object.
 *  @param cfg Pointer to the endpoint configuration.
 *
 *  @retval -EINVAL One of the parameters is incorrect.
 *  @retval other errno code reported by rpmsg.
 */
int rpmsg_mi_ept_register(struct rpmsg_mi_ctx *ctx,
			  struct rpmsg_mi_ept *ept,
			  struct rpmsg_mi_ept_cfg *cfg);

/** @brief Send data using given IPC endpoint.
 *
 *  Note: It is not possible to send a message of zero length.
 *
 *  @param ept Endpoint object.
 *  @param data Pointer to the buffer to send through RPMsg.
 *  @param len Number of bytes to send.
 *
 *  @retval Number of bytes it has sent or negative error value on failure.
 */
int rpmsg_mi_send(struct rpmsg_mi_ept *ept, const void *data, size_t len);

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_RPMSG_MULTIPLE_INNSTANCE_H_ */