/* Copyright 2020 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "console.h"
#include "host_command.h"
#include "ipi_chip.h"
#include "util.h"

#define CPRINTF(format, args...) cprintf(CC_IPI, format, ##args)
#define CPRINTS(format, args...) cprints(CC_IPI, format, ##args)

#define HOSTCMD_MAX_REQUEST_SIZE CONFIG_IPC_SHARED_OBJ_BUF_SIZE
/* Reserve 1 extra byte for HOSTCMD_TYPE and 3 bytes for padding. */
#define HOSTCMD_MAX_RESPONSE_SIZE (CONFIG_IPC_SHARED_OBJ_BUF_SIZE - 4)
#define HOSTCMD_TYPE_HOSTCMD 1
#define HOSTCMD_TYPE_HOSTEVENT 2

/*
 * hostcmd and hostevent share the same IPI ID, and use first byte type to
 * indicate its type.
 */
static struct hostcmd_data {
	const uint8_t type;
	/* To be compatible with CONFIG_HOSTCMD_ALIGNED */
	uint8_t response[HOSTCMD_MAX_RESPONSE_SIZE] __aligned(4);
} hc_cmd_obj = { .type = HOSTCMD_TYPE_HOSTCMD };
BUILD_ASSERT(sizeof(struct hostcmd_data) == CONFIG_IPC_SHARED_OBJ_BUF_SIZE);

/* Size of the rpmsg device name, should sync across kernel and EC. */
#define RPMSG_NAME_SIZE 32

/*
 * The layout of name service message.
 * This should sync across kernel and EC.
 */
struct rpmsg_ns_msg {
	/* Name of the corresponding rpmsg_driver. */
	char name[RPMSG_NAME_SIZE];
	/* IPC ID */
	uint32_t addr;
};

static void hostcmd_send_response_packet(struct host_packet *pkt)
{
	int ret;

	ret = ipi_send(SCP_IPI_HOST_COMMAND, &hc_cmd_obj,
		       pkt->response_size +
			       offsetof(struct hostcmd_data, response),
		       1);
	if (ret)
		CPRINTS("failed to %s(), ret=%d", __func__, ret);
}

static void hostcmd_handler(int32_t id, void *buf, uint32_t len)
{
	static struct host_packet packet;
	uint8_t *in_msg = buf;
	struct ec_host_request *r = (struct ec_host_request *)in_msg;
	int i;

	if (in_msg[0] != EC_HOST_REQUEST_VERSION) {
		CPRINTS("ERROR: Protocol V2 is not supported!");
		CPRINTF("in_msg=[");
		for (i = 0; i < len; i++)
			CPRINTF("%02x ", in_msg[i]);
		CPRINTF("]\n");
		return;
	}

	/* Protocol version 3 */

	packet.send_response = hostcmd_send_response_packet;

	/*
	 * Just assign the buffer to request, host_packet_receive
	 * handles the buffer copy.
	 */
	packet.request = (void *)r;
	packet.request_temp = NULL;
	packet.request_max = HOSTCMD_MAX_REQUEST_SIZE;
	packet.request_size = host_request_expected_size(r);

	packet.response = hc_cmd_obj.response;
	/* Reserve space for the preamble and trailing byte */
	packet.response_max = HOSTCMD_MAX_RESPONSE_SIZE;
	packet.response_size = 0;

	packet.driver_result = EC_RES_SUCCESS;

	host_packet_receive(&packet);
}
DECLARE_IPI(SCP_IPI_HOST_COMMAND, hostcmd_handler, 0);

/*
 * Get protocol information
 */
static enum ec_status hostcmd_get_protocol_info(struct host_cmd_handler_args *args)
{
	struct ec_response_get_protocol_info *r = args->response;

	memset(r, 0, sizeof(*r));
	r->protocol_versions |= BIT(3);
	r->max_request_packet_size = HOSTCMD_MAX_REQUEST_SIZE;
	r->max_response_packet_size = HOSTCMD_MAX_RESPONSE_SIZE;

	args->response_size = sizeof(*r);

	return EC_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, hostcmd_get_protocol_info,
		     EC_VER_MASK(0));

void hostcmd_init(void)
{
	int ret;
	struct rpmsg_ns_msg ns_msg;

	if (IS_ENABLED(CONFIG_RPMSG_NAME_SERVICE)) {
		ns_msg.addr = SCP_IPI_HOST_COMMAND;
		strncpy(ns_msg.name, "cros-ec-rpmsg", RPMSG_NAME_SIZE);
		ret = ipi_send(SCP_IPI_NS_SERVICE, &ns_msg, sizeof(ns_msg), 1);
		if (ret)
			CPRINTS("Failed to announce host command channel");
	}
}
