// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2013-2018, 2021, The Linux Foundation. All rights reserved.
 */

#include <linux/slab.h>
#include "ipa_rm_dependency_graph.h"
#include "ipa_rm_i.h"

static int ipa_rm_dep_get_index(enum ipa_rm_resource_name resource_name)
{
	int resource_index = IPA_RM_INDEX_INVALID;

	if (IPA_RM_RESORCE_IS_PROD(resource_name))
		resource_index = ipa_rm_prod_index(resource_name);
	else if (IPA_RM_RESORCE_IS_CONS(resource_name))
		resource_index = ipa_rm_cons_index(resource_name);

	return resource_index;
}

/**
 * ipa_rm_dep_graph_create() - creates graph
 * @dep_graph: [out] created dependency graph
 *
 * Returns: dependency graph on success, NULL on failure
 */
int  ipa_rm_dep_graph_create(struct ipa_rm_dep_graph **dep_graph)
{
	int result = 0;

	*dep_graph = kzalloc(sizeof(**dep_graph), GFP_KERNEL);
	if (!*dep_graph)
		result = -ENOMEM;
	return result;
}

/**
 * ipa_rm_dep_graph_delete() - destroyes the graph
 * @graph: [in] dependency graph
 *
 * Frees all resources.
 */
void ipa_rm_dep_graph_delete(struct ipa_rm_dep_graph *graph)
{
	int resource_index;

	if (!graph) {
		IPA_RM_ERR("invalid params\n");
		return;
	}
	for (resource_index = 0;
			resource_index < IPA_RM_RESOURCE_MAX;
			resource_index++)
		kfree(graph->resource_table[resource_index]);
	memset(graph->resource_table, 0, sizeof(graph->resource_table));
}

/**
 * ipa_rm_dep_graph_get_resource() - provides a resource by name
 * @graph: [in] dependency graph
 * @name: [in] name of the resource
 * @resource: [out] resource in case of success
 *
 * Returns: 0 on success, negative on failure
 */
int ipa_rm_dep_graph_get_resource(
				struct ipa_rm_dep_graph *graph,
				enum ipa_rm_resource_name resource_name,
				struct ipa_rm_resource **resource)
{
	int result;
	int resource_index;

	if (!graph) {
		result = -EINVAL;
		goto bail;
	}
	resource_index = ipa_rm_dep_get_index(resource_name);
	if (resource_index == IPA_RM_INDEX_INVALID ||
		resource_index >= IPA_RM_RESOURCE_MAX) {
		result = -EINVAL;
		goto bail;
	}
	*resource = graph->resource_table[resource_index];
	if (!*resource) {
		result = -EINVAL;
		goto bail;
	}
	result = 0;
bail:
	return result;
}

/**
 * ipa_rm_dep_graph_add() - adds resource to graph
 * @graph: [in] dependency graph
 * @resource: [in] resource to add
 *
 * Returns: 0 on success, negative on failure
 */
int ipa_rm_dep_graph_add(struct ipa_rm_dep_graph *graph,
			 struct ipa_rm_resource *resource)
{
	int result = 0;
	int resource_index;

	if (!graph || !resource) {
		result = -EINVAL;
		goto bail;
	}
	resource_index = ipa_rm_dep_get_index(resource->name);
	if (resource_index == IPA_RM_INDEX_INVALID ||
		resource_index >= IPA_RM_RESOURCE_MAX) {
		result = -EINVAL;
		goto bail;
	}
	graph->resource_table[resource_index] = resource;
bail:
	return result;
}

/**
 * ipa_rm_dep_graph_remove() - removes resource from graph
 * @graph: [in] dependency graph
 * @resource: [in] resource to add
 *
 * Returns: 0 on success, negative on failure
 */
int ipa_rm_dep_graph_remove(struct ipa_rm_dep_graph *graph,
		enum ipa_rm_resource_name resource_name)
{
	if (!graph)
		return -EINVAL;
	graph->resource_table[resource_name] = NULL;

	return 0;
}

/**
 * ipa_rm_dep_graph_add_dependency() - adds dependency between
 *				two nodes in graph
 * @graph: [in] dependency graph
 * @resource_name: [in] resource to add
 * @depends_on_name: [in] resource to add
 * @userspace_dep: [in] operation requested by userspace ?
 *
 * Returns: 0 on success, negative on failure
 */
int ipa_rm_dep_graph_add_dependency(struct ipa_rm_dep_graph *graph,
				    enum ipa_rm_resource_name resource_name,
				    enum ipa_rm_resource_name depends_on_name,
				    bool userspace_dep)
{
	struct ipa_rm_resource *dependent = NULL;
	struct ipa_rm_resource *dependency = NULL;
	int result;

	if (!graph ||
		!IPA_RM_RESORCE_IS_PROD(resource_name) ||
		!IPA_RM_RESORCE_IS_CONS(depends_on_name)) {
		IPA_RM_ERR("invalid params\n");
		result = -EINVAL;
		goto bail;
	}
	if (ipa_rm_dep_graph_get_resource(graph,
					  resource_name,
					  &dependent)) {
		IPA_RM_ERR("%s does not exist\n",
					ipa_rm_resource_str(resource_name));
		result = -EINVAL;
		goto bail;
	}
	if (ipa_rm_dep_graph_get_resource(graph,
					depends_on_name,
					  &dependency)) {
		IPA_RM_ERR("%s does not exist\n",
					ipa_rm_resource_str(depends_on_name));
		result = -EINVAL;
		goto bail;
	}
	result = ipa_rm_resource_add_dependency(dependent, dependency,
		userspace_dep);
bail:
	IPA_RM_DBG("EXIT with %d\n", result);

	return result;
}

/**
 * ipa_rm_dep_graph_delete_dependency() - deleted dependency between
 *				two nodes in graph
 * @graph: [in] dependency graph
 * @resource_name: [in] resource to delete
 * @depends_on_name: [in] resource to delete
 * @userspace_dep: [in] operation requested by userspace ?
 *
 * Returns: 0 on success, negative on failure
 *
 */
int ipa_rm_dep_graph_delete_dependency(struct ipa_rm_dep_graph *graph,
				enum ipa_rm_resource_name resource_name,
				enum ipa_rm_resource_name depends_on_name,
				bool userspace_dep)
{
	struct ipa_rm_resource *dependent = NULL;
	struct ipa_rm_resource *dependency = NULL;
	int result;

	if (!graph ||
		!IPA_RM_RESORCE_IS_PROD(resource_name) ||
		!IPA_RM_RESORCE_IS_CONS(depends_on_name)) {
		IPA_RM_ERR("invalid params\n");
		result = -EINVAL;
		goto bail;
	}

	if (ipa_rm_dep_graph_get_resource(graph,
					  resource_name,
					  &dependent)) {
		IPA_RM_DBG("%s does not exist\n",
					ipa_rm_resource_str(resource_name));
		result = -EINVAL;
		goto bail;
	}

	if (ipa_rm_dep_graph_get_resource(graph,
					  depends_on_name,
					  &dependency)) {
		IPA_RM_DBG("%s does not exist\n",
					ipa_rm_resource_str(depends_on_name));
		result = -EINVAL;
		goto bail;
	}

	result = ipa_rm_resource_delete_dependency(dependent, dependency,
		userspace_dep);
bail:
	IPA_RM_DBG("EXIT with %d\n", result);

	return result;
}
