// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2020-2021 Intel Corporation.
 */
#include <linux/vmalloc.h>

#include "iosm_ipc_coredump.h"

/**
 * ipc_coredump_collect - To collect coredump
 * @devlink:            Pointer to devlink instance.
 * @data:               Pointer to snapshot
 * @entry:              ID of requested snapshot
 * @region_size:        Region size
 *
 * Returns: 0 on success, error on failure
 */
int ipc_coredump_collect(struct iosm_devlink *devlink, u8 **data, int entry,
			 u32 region_size)
{
	int ret, bytes_to_read, bytes_read = 0, i = 0;
	s32 remaining;
	u8 *data_ptr;

	data_ptr = vmalloc(region_size);
	if (!data_ptr)
		return -ENOMEM;

	remaining = devlink->cd_file_info[entry].actual_size;
	ret = ipc_devlink_send_cmd(devlink, rpsi_cmd_coredump_get, entry);
	if (ret) {
		dev_err(devlink->dev, "Send coredump_get cmd failed");
		goto get_cd_fail;
	}
	while (remaining > 0) {
		bytes_to_read = min(remaining, MAX_DATA_SIZE);
		bytes_read = 0;
		ret = ipc_imem_sys_devlink_read(devlink, data_ptr + i,
						bytes_to_read, &bytes_read);
		if (ret) {
			dev_err(devlink->dev, "CD data read failed");
			goto get_cd_fail;
		}
		remaining -= bytes_read;
		i += bytes_read;
	}

	*data = data_ptr;

	return 0;

get_cd_fail:
	vfree(data_ptr);
	return ret;
}

/**
 * ipc_coredump_get_list - Get coredump list from modem
 * @devlink:         Pointer to devlink instance.
 * @cmd:             RPSI command to be sent
 *
 * Returns: 0 on success, error on failure
 */
int ipc_coredump_get_list(struct iosm_devlink *devlink, u16 cmd)
{
	u32 byte_read, num_entries, file_size;
	struct iosm_cd_table *cd_table;
	u8 size[MAX_SIZE_LEN], i;
	char *filename;
	int ret;

	cd_table = kzalloc(MAX_CD_LIST_SIZE, GFP_KERNEL);
	if (!cd_table) {
		ret = -ENOMEM;
		goto  cd_init_fail;
	}

	ret = ipc_devlink_send_cmd(devlink, cmd, MAX_CD_LIST_SIZE);
	if (ret) {
		dev_err(devlink->dev, "rpsi_cmd_coredump_start failed");
		goto cd_init_fail;
	}

	ret = ipc_imem_sys_devlink_read(devlink, (u8 *)cd_table,
					MAX_CD_LIST_SIZE, &byte_read);
	if (ret) {
		dev_err(devlink->dev, "Coredump data is invalid");
		goto cd_init_fail;
	}

	if (byte_read != MAX_CD_LIST_SIZE)
		goto cd_init_fail;

	if (cmd == rpsi_cmd_coredump_start) {
		num_entries = le32_to_cpu(cd_table->list.num_entries);
		if (num_entries == 0 || num_entries > IOSM_NOF_CD_REGION) {
			ret = -EINVAL;
			goto cd_init_fail;
		}

		for (i = 0; i < num_entries; i++) {
			file_size = le32_to_cpu(cd_table->list.entry[i].size);
			filename = cd_table->list.entry[i].filename;

			if (file_size > devlink->cd_file_info[i].default_size) {
				ret = -EINVAL;
				goto cd_init_fail;
			}

			devlink->cd_file_info[i].actual_size = file_size;
			dev_dbg(devlink->dev, "file: %s actual size %d",
				filename, file_size);
			devlink_flash_update_status_notify(devlink->devlink_ctx,
							   filename,
							   "FILENAME", 0, 0);
			snprintf(size, sizeof(size), "%d", file_size);
			devlink_flash_update_status_notify(devlink->devlink_ctx,
							   size, "FILE SIZE",
							   0, 0);
		}
	}

cd_init_fail:
	kfree(cd_table);
	return ret;
}