/*
 * Copyright (c) 2021, L&T Technology Services Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <errno.h>
#include <openamp/rpmsg_rpc_client_server.h>

static int rpmsg_endpoint_client_cb(struct rpmsg_endpoint *, void *, size_t,
				    uint32_t, void *);

static void rpmsg_service_client_unbind(struct rpmsg_endpoint *ept)
{
	struct rpmsg_rpc_clt *rpc;
	(void)ept;

	rpc = metal_container_of(ept, struct rpmsg_rpc_clt, ept);
	rpmsg_destroy_ept(&rpc->ept);
	if (rpc->shutdown_cb)
		rpc->shutdown_cb(rpc);
}

int rpmsg_rpc_client_init(struct rpmsg_rpc_clt *rpc,
			  struct rpmsg_device *rdev,
			  rpmsg_rpc_shutdown_cb shutdown_cb,
			  const struct rpmsg_rpc_client_services *services,
			  int len)
{
	int ret;

	if (!rpc || !rdev)
		return -EINVAL;

	rpc->services = services;
	rpc->n_services = len;

	rpc->shutdown_cb = shutdown_cb;

	ret = rpmsg_create_ept(&rpc->ept, rdev,
			       RPMSG_RPC_SERVICE_NAME, RPMSG_ADDR_ANY,
			       RPMSG_ADDR_ANY,
			       rpmsg_endpoint_client_cb,
			       rpmsg_service_client_unbind);

	return ret;
}

int rpmsg_rpc_client_send(struct rpmsg_rpc_clt *rpc,
			  uint32_t rpc_id, void *request_param,
			  size_t req_param_size)
{
	unsigned char tmpbuf[MAX_BUF_LEN];

	if (!rpc)
		return -EINVAL;

	/* to optimize with the zero copy API */
	memcpy(tmpbuf, &rpc_id, MAX_FUNC_ID_LEN);
	memcpy(&tmpbuf[MAX_FUNC_ID_LEN], request_param, req_param_size);
	return rpmsg_send(&rpc->ept, tmpbuf, MAX_FUNC_ID_LEN + req_param_size);
}

static const struct rpmsg_rpc_client_services *find_service(struct
							    rpmsg_rpc_clt * rpc,
							    uint32_t id)
{
	const struct rpmsg_rpc_client_services *service;

	for (unsigned int i = 0; i < rpc->n_services; i++) {
		service = &rpc->services[i];

		if (service->id == id) {
			return service;
		}
	}
	return NULL;
}

void rpmsg_rpc_client_release(struct rpmsg_rpc_clt *rpc)
{
	if (!rpc)
		return;
	rpmsg_destroy_ept(&rpc->ept);

}

static int rpmsg_endpoint_client_cb(struct rpmsg_endpoint *ept,
				    void *data, size_t len,
				    uint32_t src, void *priv)
{
	struct rpmsg_rpc_clt *rpc;
	const struct rpmsg_rpc_client_services *service;
	struct rpmsg_rpc_answer *msg;
	(void)priv;
	(void)src;

	if (!data || !ept)
		return -EINVAL;

	msg = (struct rpmsg_rpc_answer *)data;

	rpc = metal_container_of(ept,
				 struct rpmsg_rpc_clt,
				 ept);
	service = find_service(rpc, msg->id);
	if (!service)
		return -EINVAL;

	/* Invoke the callback function of the rpc */
	service->cb(rpc, msg->status, msg->params, len);

	return RPMSG_SUCCESS;
}
