/* Copyright 2014-2016 Freescale Semiconductor Inc.
 * Copyright 2017-2021 NXP
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * * Neither the name of the above-listed copyright holders nor the
 * names of any contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 *
 * ALTERNATIVELY, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") as published by the Free Software
 * Foundation, either version 2 of that License or (at your option) any
 * later version.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include "restool.h"
#include "utils.h"
#include "mc_v9/fsl_dpni.h"
#include "mc_v10/fsl_dpni.h"

#define ALL_DPNI_OPTS (					\
	DPNI_OPT_ALLOW_DIST_KEY_PER_TC |		\
	DPNI_OPT_TX_CONF_DISABLED |			\
	DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED |	\
	DPNI_OPT_DIST_HASH |				\
	DPNI_OPT_DIST_FS |				\
	DPNI_OPT_UNICAST_FILTER	|			\
	DPNI_OPT_MULTICAST_FILTER |			\
	DPNI_OPT_VLAN_FILTER |				\
	DPNI_OPT_IPR |					\
	DPNI_OPT_IPF |					\
	DPNI_OPT_VLAN_MANIPULATION |			\
	DPNI_OPT_QOS_MASK_SUPPORT |			\
	DPNI_OPT_FS_MASK_SUPPORT)


#define ALL_DPNI_OPTS_V10 (				\
	DPNI_OPT_TX_FRM_RELEASE |			\
	DPNI_OPT_NO_MAC_FILTER |			\
	DPNI_OPT_HAS_POLICING |				\
	DPNI_OPT_SHARED_CONGESTION |			\
	DPNI_OPT_HAS_KEY_MASKING |			\
	DPNI_OPT_NO_FS |				\
	DPNI_OPT_HAS_OPR |				\
	DPNI_OPT_OPR_PER_TC |				\
	DPNI_OPT_SINGLE_SENDER |			\
	DPNI_OPT_CUSTOM_CG |				\
	DPNI_OPT_CUSTOM_OPR |				\
	DPNI_OPT_SHARED_HASH_KEY |			\
	DPNI_OPT_SHARED_FS)

static enum mc_cmd_status mc_status;

/**
 * max_dist: Maximum distribution size for Rx traffic class;
 * supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96,
 * 112,128,192,224,256,384,448,512,768,896,1024;
 */
#define MAX_DIST_SIZE	1024

struct dpni_config {
	struct dpni_extended_cfg dpni_extended_cfg;
	struct dpni_cfg_v9 dpni_cfg;
	struct dpni_attr_v9 dpni_attr;
	uint16_t dpni_handle;
};


#define RESTOOL_DPNI_CREATE \
	_IOWR(RESTOOL_IOCTL_TYPE, 0x5, struct dpni_config)

#define RESTOOL_DPNI_GET_ATTRIBUTES \
	_IOWR(RESTOOL_IOCTL_TYPE, 0x6, struct dpni_config)


/**
 * dpni info command options
 */
enum dpni_info_options {
	INFO_OPT_HELP = 0,
	INFO_OPT_VERBOSE,
};

static struct option dpni_info_options[] = {
	[INFO_OPT_HELP] = {
		.name = "help",
		.has_arg = 0,
		.flag = NULL,
		.val = 0,
	},

	[INFO_OPT_VERBOSE] = {
		.name = "verbose",
		.has_arg = 0,
		.flag = NULL,
		.val = 0,
	},

	{ 0 },
};

C_ASSERT(ARRAY_SIZE(dpni_info_options) <= MAX_NUM_CMD_LINE_OPTIONS + 1);

/**
 * dpni create command options
 */
enum dpni_create_options {
	CREATE_OPT_HELP = 0,
	CREATE_OPT_OPTIONS,
	CREATE_OPT_MAC_ADDR,
	CREATE_OPT_MAX_SENDERS,
	CREATE_OPT_MAX_TCS,
	CREATE_OPT_MAX_DIST_PER_TC,
	CREATE_OPT_MAX_FS_ENTRIES_PER_TC,
	CREATE_OPT_MAX_UNICAST_FILTERS,
	CREATE_OPT_MAX_MULTICAST_FILTERS,
	CREATE_OPT_MAX_VLAN_FILTERS,
	CREATE_OPT_MAX_QOS_ENTRIES,
	CREATE_OPT_MAX_QOS_KEY_SIZE,
	CREATE_OPT_MAX_DIST_KEY_SIZE,
	CREATE_OPT_NUM_QUEUES,
	CREATE_OPT_NUM_TCS,
	CREATE_OPT_MAC_ENTRIES,
	CREATE_OPT_VLAN_ENTRIES,
	CREATE_OPT_QOS_ENTRIES,
	CREATE_OPT_FS_ENTRIES,
	CREATE_OPT_PARENT_DPRC,
	CREATE_OPT_MAC_FILTER_ENTRIES,
	CREATE_OPT_VLAN_FILTER_ENTRIES,
	CREATE_OPT_NUM_CGS,
	CREATE_OPT_DIST_KEY_SIZE,
	CREATE_OPT_NUM_CHANNELS,
	CREATE_OPT_NUM_OPR,
};

static struct option dpni_create_options[] = {
	[CREATE_OPT_HELP] = {
		.name = "help",
		.has_arg = 0,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_OPTIONS] = {
		.name = "options",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAC_ADDR] = {
		.name = "mac-addr",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_SENDERS] = {
		.name = "max-senders",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_TCS] = {
		.name = "max-tcs",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_DIST_PER_TC] = {
		.name = "max-dist-per-tc",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_FS_ENTRIES_PER_TC] = {
		.name = "max-fs-entries-per-tc",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_UNICAST_FILTERS] = {
		.name = "max-unicast-filters",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_MULTICAST_FILTERS] = {
		.name = "max-multicast-filters",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_VLAN_FILTERS] = {
		.name = "max-vlan-filters",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_QOS_ENTRIES] = {
		.name = "max-qos-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_QOS_KEY_SIZE] = {
		.name = "max-qos-key-size",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAX_DIST_KEY_SIZE] = {
		.name = "max-dist-key-size",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_NUM_QUEUES] = {
		.name = "num-queues",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_NUM_TCS] = {
		.name = "num-tcs",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAC_ENTRIES] = {
		.name = "mac-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_VLAN_ENTRIES] = {
		.name = "vlan-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_QOS_ENTRIES] = {
		.name = "qos-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_FS_ENTRIES] = {
		.name = "fs-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_PARENT_DPRC] = {
		.name = "container",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_MAC_FILTER_ENTRIES] = {
		.name = "mac-filter-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_VLAN_FILTER_ENTRIES] = {
		.name = "vlan-filter-entries",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_NUM_CGS] = {
		.name = "num-cgs",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_DIST_KEY_SIZE] = {
		.name = "dist-key-size",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_NUM_CHANNELS] = {
		.name = "num-channels",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	[CREATE_OPT_NUM_OPR] = {
		.name = "num-opr",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	{ 0 },
};

C_ASSERT(ARRAY_SIZE(dpni_create_options) <= MAX_NUM_CMD_LINE_OPTIONS + 1);

/**
 * dpni destroy command options
 */
enum dpni_destroy_options {
	DESTROY_OPT_HELP = 0,
};

static struct option dpni_destroy_options[] = {
	[DESTROY_OPT_HELP] = {
		.name = "help",
		.has_arg = 0,
		.flag = NULL,
		.val = 0,
	},

	{ 0 },
};

C_ASSERT(ARRAY_SIZE(dpni_destroy_options) <= MAX_NUM_CMD_LINE_OPTIONS + 1);

enum dpni_update_options_v10 {
	UPDATE_OPT_HELP = 0,
	UPDATE_MAC_ADDR,
};

static struct option dpni_update_options_v10[] = {
	[UPDATE_OPT_HELP] = {
		.name = "help",
		.has_arg = 0,
		.flag = NULL,
		.val = 0,
	},

	[UPDATE_MAC_ADDR] = {
		.name = "mac-addr",
		.has_arg = 1,
		.flag = NULL,
		.val = 0,
	},

	{ 0 },
};

C_ASSERT(ARRAY_SIZE(dpni_update_options_v10) <= MAX_NUM_CMD_LINE_OPTIONS + 1);

const struct flib_ops dpni_ops = {
	.obj_open = dpni_open_v10,
	.obj_close = dpni_close_v10,
	.obj_get_irq_mask = dpni_get_irq_mask_v10,
	.obj_get_irq_status = dpni_get_irq_status_v10,
};

static struct option_entry options_map_v9[] = {
	OPTION_MAP_ENTRY(DPNI_OPT_ALLOW_DIST_KEY_PER_TC),
	OPTION_MAP_ENTRY(DPNI_OPT_TX_CONF_DISABLED),
	OPTION_MAP_ENTRY(DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED),
	OPTION_MAP_ENTRY(DPNI_OPT_DIST_HASH),
	OPTION_MAP_ENTRY(DPNI_OPT_DIST_FS),
	OPTION_MAP_ENTRY(DPNI_OPT_UNICAST_FILTER),
	OPTION_MAP_ENTRY(DPNI_OPT_MULTICAST_FILTER),
	OPTION_MAP_ENTRY(DPNI_OPT_VLAN_FILTER),
	OPTION_MAP_ENTRY(DPNI_OPT_IPR),
	OPTION_MAP_ENTRY(DPNI_OPT_IPF),
	OPTION_MAP_ENTRY(DPNI_OPT_VLAN_MANIPULATION),
	OPTION_MAP_ENTRY(DPNI_OPT_QOS_MASK_SUPPORT),
	OPTION_MAP_ENTRY(DPNI_OPT_FS_MASK_SUPPORT),
};
static unsigned int options_num_v9 = ARRAY_SIZE(options_map_v9);

static struct option_entry options_map_v10_0[] = {
	OPTION_MAP_ENTRY(DPNI_OPT_TX_FRM_RELEASE),
	OPTION_MAP_ENTRY(DPNI_OPT_NO_MAC_FILTER),
	OPTION_MAP_ENTRY(DPNI_OPT_HAS_POLICING),
	OPTION_MAP_ENTRY(DPNI_OPT_SHARED_CONGESTION),
	OPTION_MAP_ENTRY(DPNI_OPT_HAS_KEY_MASKING),
	OPTION_MAP_ENTRY(DPNI_OPT_NO_FS),
};
static unsigned int options_num_v10_0 = ARRAY_SIZE(options_map_v10_0);

static struct option_entry options_map_v10_1[] = {
	OPTION_MAP_ENTRY(DPNI_OPT_TX_FRM_RELEASE),
	OPTION_MAP_ENTRY(DPNI_OPT_NO_MAC_FILTER),
	OPTION_MAP_ENTRY(DPNI_OPT_HAS_POLICING),
	OPTION_MAP_ENTRY(DPNI_OPT_SHARED_CONGESTION),
	OPTION_MAP_ENTRY(DPNI_OPT_HAS_KEY_MASKING),
	OPTION_MAP_ENTRY(DPNI_OPT_NO_FS),
	OPTION_MAP_ENTRY(DPNI_OPT_HAS_OPR),
	OPTION_MAP_ENTRY(DPNI_OPT_OPR_PER_TC),
	OPTION_MAP_ENTRY(DPNI_OPT_SINGLE_SENDER),
	OPTION_MAP_ENTRY(DPNI_OPT_CUSTOM_CG),
	OPTION_MAP_ENTRY(DPNI_OPT_CUSTOM_OPR),
	OPTION_MAP_ENTRY(DPNI_OPT_SHARED_HASH_KEY),
	OPTION_MAP_ENTRY(DPNI_OPT_SHARED_FS),
};
static unsigned int options_num_v10_1 = ARRAY_SIZE(options_map_v10_1);

#define DPNI_STATS_PER_PAGE_V10 6

static const char *dpni_stats_v10[][DPNI_STATS_PER_PAGE_V10] = {
	{
	"ingress_all_frames",
	"ingress_all_bytes",
	"ingress_multicast_frames",
	"ingress_multicast_bytes",
	"ingress_broadcast_frames",
	"ingress_broadcast_bytes"
	}, {
	"egress_all_frames",
	"egress_all_bytes",
	"egress_multicast_frames",
	"egress_multicast_bytes",
	"egress_broadcast_frames",
	"egress_broadcast_bytes"
	}, {
	"ingress_filtered_frames",
	"ingress_discarded_frames",
	"ingress_nobuffer_discards",
	"egress_discarded_frames",
	"egress_confirmed_frames",
	""
	}, {
	"ceetm_dequeue_bytes",
	"ceetm_dequeue_frames",
	"ceetm_reject_bytes",
	"ceetm_reject_frames",
	""
	}, {
	"cgr_reject_frames",
	"cgr_reject_bytes",
	""
	}, {
	"policer_cnt_red",
	"policer_cnt_yellow",
	"policer_cnt_green",
	"policer_cnt_re_red",
	"policer_cnt_re_yellow",
	""
	}, {
	"tx_pending_frames_cnt",
	""
	}
};

static int cmd_dpni_help(void)
{
	static const char help_msg[] =
		"\n"
		"Usage: restool dpni <command> [--help] [ARGS...]\n"
		"Where <command> can be:\n"
		"   info - displays detailed information about a DPNI object.\n"
		"   create - creates a new child DPNI under the root DPRC.\n"
		"   destroy - destroys a child DPNI under the root DPRC.\n"
		"\n"
		"For command-specific help, use the --help option of each command.\n"
		"\n";

	printf(help_msg);
	return 0;
}

static int cmd_dpni_help_v10(void)
{
	static const char help_msg[] =
		"\n"
		"Usage: restool dpni <command> [--help] [ARGS...]\n"
		"Where <command> can be:\n"
		"   info - displays detailed information about a DPNI object.\n"
		"   create - creates a new child DPNI under the root DPRC.\n"
		"   destroy - destroys a child DPNI under the root DPRC.\n"
		"   update - update attributes of already created DPNI.\n"
		"\n"
		"For command-specific help, use the --help option of each command.\n"
		"\n";

	printf(help_msg);
	return 0;
}


static void print_dpni_options(uint32_t options)
{
	if ((options & ~ALL_DPNI_OPTS) != 0) {
		printf("\tUnrecognized options found...\n");
		return;
	}

	if (options & DPNI_OPT_ALLOW_DIST_KEY_PER_TC)
		printf("\tDPNI_OPT_ALLOW_DIST_KEY_PER_TC\n");

	if (options & DPNI_OPT_TX_CONF_DISABLED)
		printf("\tDPNI_OPT_TX_CONF_DISABLED\n");

	if (options & DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED)
		printf("\tDPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED\n");

	if (options & DPNI_OPT_DIST_HASH)
		printf("\tDPNI_OPT_DIST_HASH\n");

	if (options & DPNI_OPT_DIST_FS)
		printf("\tDPNI_OPT_DIST_FS\n");

	if (options & DPNI_OPT_UNICAST_FILTER)
		printf("\tDPNI_OPT_UNICAST_FILTER\n");

	if (options & DPNI_OPT_MULTICAST_FILTER)
		printf("\tDPNI_OPT_MULTICAST_FILTER\n");

	if (options & DPNI_OPT_VLAN_FILTER)
		printf("\tDPNI_OPT_VLAN_FILTER\n");

	if (options & DPNI_OPT_IPR)
		printf("\tDPNI_OPT_IPR\n");

	if (options & DPNI_OPT_IPF)
		printf("\tDPNI_OPT_IPF\n");

	if (options & DPNI_OPT_VLAN_MANIPULATION)
		printf("\tDPNI_OPT_VLAN_MANIPULATION\n");

	if (options & DPNI_OPT_QOS_MASK_SUPPORT)
		printf("\tDPNI_OPT_QOS_MASK_SUPPORT\n");

	if (options & DPNI_OPT_FS_MASK_SUPPORT)
		printf("\tDPNI_OPT_FS_MASK_SUPPORT\n");
}

static void print_dpni_options_v10(uint32_t options)
{
	if ((options & ~ALL_DPNI_OPTS_V10) != 0) {
		printf("\tUnrecognized options found...\n");
		return;
	}

	if (options & DPNI_OPT_TX_FRM_RELEASE)
		printf("\tDPNI_OPT_TX_FRM_RELEASE\n");

	if (options & DPNI_OPT_NO_MAC_FILTER)
		printf("\tDPNI_OPT_NO_MAC_FILTER\n");

	if (options & DPNI_OPT_HAS_POLICING)
		printf("\tDPNI_OPT_HAS_POLICING\n");

	if (options & DPNI_OPT_SHARED_CONGESTION)
		printf("\tDPNI_OPT_SHARED_CONGESTION\n");

	if (options & DPNI_OPT_HAS_KEY_MASKING)
		printf("\tDPNI_OPT_HAS_KEY_MASKING\n");

	if (options & DPNI_OPT_NO_FS)
		printf("\tDPNI_OPT_NO_FS\n");

	if (options & DPNI_OPT_HAS_OPR)
		printf("\tDPNI_OPT_HAS_OPR\n");

	if (options & DPNI_OPT_OPR_PER_TC)
		printf("\tDPNI_OPT_OPR_PER_TC\n");

	if (options & DPNI_OPT_SINGLE_SENDER)
		printf("\tDPNI_OPT_SINGLE_SENDER\n");

	if (options & DPNI_OPT_CUSTOM_CG)
		printf("\tDPNI_OPT_CUSTOM_CG\n");

	if (options & DPNI_OPT_CUSTOM_OPR)
		printf("\tDPNI_OPT_CUSTOM_OPR\n");

	if (options & DPNI_OPT_SHARED_HASH_KEY)
		printf("\tDPNI_OPT_SHARED_HASH_KEY\n");

	if (options & DPNI_OPT_SHARED_FS)
		printf("\tDPNI_OPT_SHARED_FS\n");
}

static int print_dpni_endpoint(uint32_t target_id)
{
	struct dprc_endpoint endpoint1;
	struct dprc_endpoint endpoint2;
	int state;
	int error = 0;

	memset(&endpoint1, 0, sizeof(struct dprc_endpoint));
	memset(&endpoint2, 0, sizeof(struct dprc_endpoint));

	strncpy(endpoint1.type, "dpni", EP_OBJ_TYPE_MAX_LEN);
	endpoint1.type[EP_OBJ_TYPE_MAX_LEN] = '\0';
	endpoint1.id = target_id;
	endpoint1.if_id = 0;

	error = dprc_get_connection(&restool.mc_io, 0,
					restool.root_dprc_handle,
					&endpoint1, &endpoint2, &state);
	printf("endpoint state: %d\n", state);

	if (error == 0 && state == -1) {
		printf("endpoint: No object associated\n");
	} else if (error == 0) {
		if (strcmp(endpoint2.type, "dpsw") == 0 ||
		    strcmp(endpoint2.type, "dpdmux") == 0) {
			printf("endpoint: %s.%d.%d",
				endpoint2.type, endpoint2.id,
				endpoint2.if_id);
		} else if (endpoint2.if_id == 0) {
			printf("endpoint: %s.%d",
				endpoint2.type, endpoint2.id);
		}

		if (state == 1)
			printf(", link is up\n");
		else if (state == 0)
			printf(", link is down\n");
		else
			printf(", link is in error state\n");

	} else {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			mc_status_to_string(mc_status), mc_status);
		return error;
	}

	return 0;
}

static void print_mac_address(uint8_t mac_addr[6])
{
	printf("mac address: ");
	for (int j = 0; j < 5; ++j)
		printf("%02x:", mac_addr[j]);
	printf("%02x\n", mac_addr[5]);
}

static int print_dpni_attr_v9(uint32_t dpni_id,
			      struct dprc_obj_desc *target_obj_desc)
{
	uint16_t dpni_handle;
	int error;
	int i;
	struct dpni_attr_v9 dpni_attr;
	struct dpni_extended_cfg ext_cfg;
	uint8_t mac_addr[6];
	bool dpni_opened = false;
	struct dpni_link_state link_state;

	error = dpni_open(&restool.mc_io, 0, dpni_id, &dpni_handle);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}
	dpni_opened = true;
	if (0 == dpni_handle) {
		DEBUG_PRINTF(
			"dpni_open() returned invalid handle (auth 0) for dpni.%u\n",
			dpni_id);
		error = -ENOENT;
		goto out;
	}

	memset(&dpni_attr, 0, sizeof(dpni_attr));

	error = dpni_get_attributes_v9(&restool.mc_io, 0, dpni_handle,
				       &dpni_attr, &ext_cfg);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	assert(dpni_id == (uint32_t)dpni_attr.id);
	assert(DPNI_MAX_TC >= dpni_attr.max_tcs);

	error = dpni_get_primary_mac_addr(&restool.mc_io, 0,
					dpni_handle, mac_addr);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	memset(&link_state, 0, sizeof(link_state));
	error = dpni_get_link_state(&restool.mc_io, 0, dpni_handle,
					&link_state);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	printf("dpni version: %u.%u\n", dpni_attr.version.major,
	       dpni_attr.version.minor);
	printf("dpni id: %d\n", dpni_attr.id);
	printf("plugged state: %splugged\n",
		(target_obj_desc->state & DPRC_OBJ_STATE_PLUGGED) ? "" : "un");
	print_dpni_endpoint(dpni_id);
	printf("link status: %d - ", link_state.up);
	link_state.up == 0 ? printf("down\n") :
	link_state.up == 1 ? printf("up\n") : printf("error state\n");
	print_mac_address(mac_addr);
	printf("dpni_attr.options value is: %#lx\n",
	       (unsigned long)dpni_attr.options);
	print_dpni_options(dpni_attr.options);
	printf("max senders: %u\n", (uint32_t)dpni_attr.max_senders);
	printf("max traffic classes: %u\n", (uint32_t)dpni_attr.max_tcs);
	for (i = 0; i < dpni_attr.max_tcs; i++)
		printf("\ttc[%d]: max_dist=%d, max_fs_entries=%d\n",
			i,
			ext_cfg.tc_cfg[i].max_dist,
			dpni_attr.options & DPNI_OPT_DIST_FS ?
				ext_cfg.tc_cfg[i].max_fs_entries : 0);
	printf("max unicast filters: %u\n",
	       (uint32_t)dpni_attr.max_unicast_filters);
	printf("max multicast filters: %u\n",
	       (uint32_t)dpni_attr.max_multicast_filters);
	printf("max vlan filters: %u\n", (uint32_t)dpni_attr.max_vlan_filters);
	printf("max QoS entries: %u\n", (uint32_t)dpni_attr.max_qos_entries);
	printf("max QoS key size: %u\n", (uint32_t)dpni_attr.max_qos_key_size);
	printf("max distribution key size: %u\n",
	       (uint32_t)dpni_attr.max_dist_key_size);
	printf("max policers: %u\n", (uint32_t)dpni_attr.max_policers);
	printf("max congestion control: %u\n",
		(uint32_t)dpni_attr.max_congestion_ctrl);

	printf("max_dist per RX traffic class:\n");
	for (int k = 0; k < dpni_attr.max_tcs; ++k)
		printf("\tclass %d's max_dist: %u\n", k,
		       (uint32_t)ext_cfg.tc_cfg[k].max_dist);

	printf("max_fs_entries per RX traffic class:\n");
	for (int m = 0; m < dpni_attr.max_tcs; ++m)
		printf("\tclass %d's max_fs_entries: %u\n", m,
		       (uint32_t)ext_cfg.tc_cfg[m].max_fs_entries);

	printf("max_reass_frm_size: %u\n",
		(uint32_t)ext_cfg.ipr_cfg.max_reass_frm_size);
	printf("min_frag_size_ipv4: %u\n",
		(uint32_t)ext_cfg.ipr_cfg.min_frag_size_ipv4);
	printf("min_frag_size_ipv6: %u\n",
		(uint32_t)ext_cfg.ipr_cfg.min_frag_size_ipv6);
	printf("max_open_frames_ipv4: %u\n",
		(uint32_t)ext_cfg.ipr_cfg.max_open_frames_ipv4);
	printf("max_open_frames_ipv6: %u\n",
		(uint32_t)ext_cfg.ipr_cfg.max_open_frames_ipv6);

	print_obj_label(target_obj_desc);

	error = 0;

out:
	if (dpni_opened) {
		int error2;

		error2 = dpni_close(&restool.mc_io, 0, dpni_handle);
		if (error2 < 0) {
			mc_status = flib_error_to_mc_status(error2);
			ERROR_PRINTF("MC error: %s (status %#x)\n",
				     mc_status_to_string(mc_status), mc_status);
			if (error == 0)
				error = error2;
		}
	}

	return error;
}

static void dpni_print_stats(const char *strings[],
			     union dpni_statistics_v10 dpni_stats)
{
	uint64_t *stat;
	int i;

	stat = (uint64_t *)&dpni_stats.raw;
	for (i = 0; i < DPNI_STATS_PER_PAGE_V10; i++) {
		if (strcmp(strings[i], "\0") == 0)
			break;
		printf("%s: %lu\n", strings[i], *stat);
		stat++;
	}
}

static int print_dpni_attr_v10(uint32_t dpni_id,
			      struct dprc_obj_desc *target_obj_desc)
{
	struct dpni_attr_v10 dpni_attr;
	union dpni_statistics_v10 dpni_stats;
	uint16_t dpni_handle, dpni_major, dpni_minor;
	struct dpni_link_state_v10 link_state;
	bool dpni_opened = false;
	uint8_t mac_addr[6];
	int error = 0;
	int error2;
	unsigned int page;
	uint16_t max_frame_length;

	error = dpni_open_v10(&restool.mc_io, 0, dpni_id, &dpni_handle);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}
	dpni_opened = true;
	if (0 == dpni_handle) {
		DEBUG_PRINTF(
			"dpni_open() returned invalid handle (auth 0) for dpni.%u\n",
			dpni_id);
		error = -ENOENT;
		goto out;
	}

	memset(&dpni_attr, 0, sizeof(dpni_attr));
	error = dpni_get_attributes_v10(&restool.mc_io, 0,
					dpni_handle, &dpni_attr);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	error = dpni_get_api_version_v10(&restool.mc_io, 0,
					 &dpni_major, &dpni_minor);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	error = dpni_get_primary_mac_addr_v10(&restool.mc_io, 0,
					      dpni_handle, mac_addr);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	memset(&link_state, 0, sizeof(link_state));
	error = dpni_get_link_state_v10(&restool.mc_io, 0, dpni_handle,
					&link_state);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}

	printf("dpni version: %u.%u\n", dpni_major, dpni_minor);
	printf("dpni id: %d\n", dpni_id);

	printf("plugged state: %splugged\n",
		(target_obj_desc->state & DPRC_OBJ_STATE_PLUGGED) ? "" : "un");
	print_dpni_endpoint(dpni_id);
	printf("link status: %d - ", link_state.up);
	link_state.up == 0 ? printf("down\n") :
	link_state.up == 1 ? printf("up\n") : printf("error state\n");

	print_mac_address(mac_addr);

	error = dpni_get_max_frame_length(&restool.mc_io, 0, dpni_handle,
					  &max_frame_length);
	if (error)
		return error;
	printf("max frame length: %hu\n", max_frame_length);

	printf("dpni_attr.options value is: %#lx\n",
	       (unsigned long)dpni_attr.options);
	print_dpni_options_v10(dpni_attr.options);

	printf("num_queues: %u\n", (uint32_t)dpni_attr.num_queues);
	printf("num_cgs: %u\n", (uint32_t)dpni_attr.num_cgs);
	printf("num_rx_tcs: %u\n", (uint32_t)dpni_attr.num_rx_tcs);
	printf("num_tx_tcs: %u\n", (uint32_t)dpni_attr.num_tx_tcs);
	printf("mac_entries: %u\n",
	       (uint32_t)dpni_attr.mac_filter_entries);
	printf("vlan_entries: %u\n",
	       (uint32_t)dpni_attr.vlan_filter_entries);
	printf("qos_entries: %u\n", (uint32_t)dpni_attr.qos_entries);
	printf("fs_entries: %u\n", (uint32_t)dpni_attr.fs_entries);
	printf("qos_key_size: %u\n", (uint32_t)dpni_attr.qos_key_size);
	printf("fs_key_size: %u\n", (uint32_t)dpni_attr.fs_key_size);
	printf("num_channels: %u\n", (uint32_t)dpni_attr.num_ceetm_ch);
	printf("num_opr: %u\n", (uint32_t)dpni_attr.num_opr);

	for (page = 0; page < 7; page++) {
		memset(&dpni_stats, 0, sizeof(dpni_stats));

		if (page == 3) {
			int ch;

			for (ch = 0; ch < dpni_attr.num_ceetm_ch; ch++) {
				int tc;

				for (tc = 0; tc < dpni_attr.num_tx_tcs; tc++) {
					uint16_t param = (ch << 8) | tc;

					memset(&dpni_stats, 0, sizeof(dpni_stats));
					error = dpni_get_statistics_v10(&restool.mc_io, 0,
								dpni_handle, page, param, &dpni_stats);
					printf("ceetm stats channel %d, TC %d\n", ch, tc);
					dpni_print_stats(dpni_stats_v10[page], dpni_stats);
				}
			}
		} else {
				error = dpni_get_statistics_v10(&restool.mc_io, 0,
								dpni_handle, page,
								0, &dpni_stats);
				dpni_print_stats(dpni_stats_v10[page], dpni_stats);
		}
	}

	print_obj_label(target_obj_desc);

out:
	if (dpni_opened) {

		error2 = dpni_close_v10(&restool.mc_io, 0, dpni_handle);
		if (error2 < 0) {
			mc_status = flib_error_to_mc_status(error2);
			ERROR_PRINTF("MC error: %s (status %#x)\n",
				     mc_status_to_string(mc_status), mc_status);
			if (error == 0)
				error = error2;
		}
	}

	return error;
}

static int print_dpni_info(uint32_t dpni_id, int mc_fw_version)
{
	int error;
	struct dprc_obj_desc target_obj_desc;
	uint32_t target_parent_dprc_id;
	bool found = false;

	memset(&target_obj_desc, 0, sizeof(struct dprc_obj_desc));
	error = find_target_obj_desc(restool.root_dprc_id,
				restool.root_dprc_handle, 0, dpni_id,
				"dpni", &target_obj_desc,
				&target_parent_dprc_id, &found);
	if (error < 0)
		goto out;

	if (strcmp(target_obj_desc.type, "dpni")) {
		printf("dpni.%d does not exist\n", dpni_id);
		return -EINVAL;
	}

	if (mc_fw_version == MC_FW_VERSION_9)
		error = print_dpni_attr_v9(dpni_id, &target_obj_desc);
	else if (mc_fw_version == MC_FW_VERSION_10)
		error = print_dpni_attr_v10(dpni_id, &target_obj_desc);
	if (error < 0)
		goto out;

	if (restool.cmd_option_mask & ONE_BIT_MASK(INFO_OPT_VERBOSE)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(INFO_OPT_VERBOSE);
		error = print_obj_verbose(&target_obj_desc, &dpni_ops);
	}

out:
	return error;
}

static int info_dpni(int mc_fw_version)
{
	static const char usage_msg[] =
		"\n"
		"Usage: restool dpni info <dpni-object> [--verbose]\n"
		"\n"
		"OPTIONS:\n"
		"--verbose\n"
		"   Shows extended/verbose information about the object\n"
		"\n"
		"EXAMPLE:\n"
		"Display information about dpni.5:\n"
		"   $ restool dpni info dpni.5\n"
		"\n";

	uint32_t obj_id;
	int error;

	if (restool.cmd_option_mask & ONE_BIT_MASK(INFO_OPT_HELP)) {
		puts(usage_msg);
		restool.cmd_option_mask &= ~ONE_BIT_MASK(INFO_OPT_HELP);
		error = 0;
		goto out;
	}

	if (restool.obj_name == NULL) {
		ERROR_PRINTF("<object> argument missing\n");
		puts(usage_msg);
		error = -EINVAL;
		goto out;
	}

	error = parse_object_name(restool.obj_name, "dpni", &obj_id);
	if (error < 0)
		goto out;

	error = print_dpni_info(obj_id, mc_fw_version);

out:
	return error;
}

static int cmd_dpni_info_v9(void)
{
	return info_dpni(MC_FW_VERSION_9);
}

static int cmd_dpni_info_v10(void)
{
	return info_dpni(MC_FW_VERSION_10);
}

static int parse_dpni_mac_addr(char *mac_addr_str, uint8_t *mac_addr)
{
	char *cursor = NULL;
	char *endptr;
	char *mac_str = strtok_r(mac_addr_str, ":", &cursor);
	long val;
	int i = 0;

	while (mac_str != NULL) {
		if (i >= 6) { /* mac address is more than 6 parts */
			ERROR_PRINTF("Invalid MAC address.\n");
			ERROR_PRINTF(
				"Please enter 48 bits MAC address, eg. 00:0e:0c:55:12:03\n");
			return -EINVAL;
		}

		errno = 0;
		val = strtol(mac_str, &endptr, 16);

		if (STRTOL_ERROR(mac_str, endptr, val, errno) ||
		    (val < 0 || val > UINT8_MAX)) {
			ERROR_PRINTF("Invalid MAC address.\n");
			ERROR_PRINTF(
				"Please enter 48 bits MAC address, eg. 00:0e:0c:55:12:03\n");
			return -EINVAL;
		}

		mac_addr[i] = (uint8_t)val;
		mac_str = strtok_r(NULL, ":", &cursor);
		++i;
	}

	if (6 != i) { /* mac address is less than 6 parts */
		ERROR_PRINTF("Invalid MAC address.\n");
		ERROR_PRINTF(
			"Please enter 48 bits MAC address, eg. 00:0e:0c:55:12:03\n");
		return -EINVAL;
	}

	return 0;
}

static int parse_dpni_max_dist_per_tc_v9(char *max_dist_per_tc_str,
	struct dpni_extended_cfg *dpni_extended_cfg, uint8_t max_tcs)
{
	char *cursor = NULL;
	char *endptr;
	char *max_dist_str = strtok_r(max_dist_per_tc_str, ",", &cursor);
	int i = 0;
	long val;

	while (max_dist_str != NULL) {
		if (i >= DPNI_MAX_TC) {
			ERROR_PRINTF("Invalid max-dist-per-tc\n");
			ERROR_PRINTF("maximum number of traffic class <= %d\n",
			     DPNI_MAX_TC);
			return -EINVAL;
		}

		errno = 0;
		val = strtol(max_dist_str, &endptr, 0);

		if (STRTOL_ERROR(max_dist_str, endptr, val, errno) ||
		    (val < 0 || val > MAX_DIST_SIZE)) {
			ERROR_PRINTF("Invalid dist-size.\n");
			return -EINVAL;
		}

		dpni_extended_cfg->tc_cfg[i].max_dist = (uint16_t)val;
		max_dist_str = strtok_r(NULL, ",", &cursor);
		++i;
	}

	if (max_tcs != i) {
		ERROR_PRINTF(
			"size of max_dist_per_tc does not match max_tcs: %u\n",
			(unsigned int)max_tcs);
		return -EINVAL;
	}

	return 0;
}

static int parse_dpni_max_fs_entries_per_tc(char *max_fs_entries_per_tc_str,
	struct dpni_extended_cfg *dpni_extended_cfg, uint8_t max_tcs)
{
	char *cursor = NULL;
	char *endptr;
	char *max_fs_entries_str =
			strtok_r(max_fs_entries_per_tc_str, ",", &cursor);
	int i = 0;
	long val;

	while (max_fs_entries_str != NULL) {
		if (i >= DPNI_MAX_TC) {
			ERROR_PRINTF("Invalid max-dist-per-tc\n");
			ERROR_PRINTF("maximum number of traffic class <= %d\n",
			     DPNI_MAX_TC);
			return -EINVAL;
		}

		errno = 0;
		val = strtol(max_fs_entries_str, &endptr, 0);

		if (STRTOL_ERROR(max_fs_entries_str, endptr, val, errno) ||
		    (val < 0 || val > MAX_DIST_SIZE)) {
			ERROR_PRINTF("Invalid dist-size.\n");
			return -EINVAL;
		}

		dpni_extended_cfg->tc_cfg[i].max_fs_entries = (uint16_t)val;
		max_fs_entries_str = strtok_r(NULL, ",", &cursor);
		++i;
	}

	if (max_tcs != i) {
		ERROR_PRINTF(
			"size of max_fs_entries_per_tc does not match max_tcs: %u\n",
			(unsigned int)max_tcs);
		return -EINVAL;
	}

	return 0;
}

static int create_dpni_v9(const char *usage_msg)
{
	struct dpni_extended_cfg dpni_extended_cfg;
	struct dpni_attr_v9 dpni_attr;
	struct dpni_cfg_v9 dpni_cfg;
	uint16_t dpni_handle;
	int error;
	long val;

	memset(&dpni_cfg, 0, sizeof(dpni_cfg));
	memset(&dpni_extended_cfg, 0, sizeof(dpni_extended_cfg));

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_HELP)) {
		puts(usage_msg);
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_HELP);
		return 0;
	}

	if (restool.obj_name != NULL) {
		ERROR_PRINTF("Unexpected argument: \'%s\'\n\n",
			     restool.obj_name);
		puts(usage_msg);
		return -EINVAL;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_OPTIONS)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_OPTIONS);
		error = parse_generic_create_options(
				restool.cmd_option_args[CREATE_OPT_OPTIONS],
				(uint64_t *)&dpni_cfg.adv.options,
				options_map_v9,
				options_num_v9);
		if (error < 0) {
			DEBUG_PRINTF(
				"parse_generic_create_options() failed with error %d, cannot get options-mask\n",
				error);
			return error;
		}
	} else {
		dpni_cfg.adv.options = DPNI_OPT_UNICAST_FILTER |
				       DPNI_OPT_MULTICAST_FILTER;
	}

	if (!(restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAC_ADDR))) {
		ERROR_PRINTF("--mac-addr option missing\n");
		puts(usage_msg);
		return -EINVAL;
	}

	restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_MAC_ADDR);
	error  = parse_dpni_mac_addr(
			restool.cmd_option_args[CREATE_OPT_MAC_ADDR],
			dpni_cfg.mac_addr);
	if (error < 0) {
		DEBUG_PRINTF(
			"parse_dpni_mac_addr() failed with error %d, cannot get mac address\n",
			error);
		return error;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAX_TCS)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_MAX_TCS);
		error = get_option_value(CREATE_OPT_MAX_TCS, &val,
					 "Invalid max tcs", 0, DPNI_MAX_TC);
		if (error)
			return error;
		dpni_cfg.adv.max_tcs = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_tcs = 1;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_DIST_PER_TC)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_DIST_PER_TC);
		error = parse_dpni_max_dist_per_tc_v9(
			restool.cmd_option_args[CREATE_OPT_MAX_DIST_PER_TC],
			&dpni_extended_cfg,
			dpni_cfg.adv.max_tcs);
		if (error < 0) {
			DEBUG_PRINTF(
				"parse_dpni_max_dist_per_tc_v9() failed with error %d, cannot get maximum distribution's size per RX traffic-class\n",
				error);
			return error;
		}
	} else {
		for (int i = 0; i < dpni_cfg.adv.max_tcs; ++i)
			dpni_extended_cfg.tc_cfg[i].max_dist = 1;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_FS_ENTRIES_PER_TC)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_FS_ENTRIES_PER_TC);
		error = parse_dpni_max_fs_entries_per_tc(
		restool.cmd_option_args[CREATE_OPT_MAX_FS_ENTRIES_PER_TC],
			&dpni_extended_cfg,
			dpni_cfg.adv.max_tcs);
		if (error < 0) {
			DEBUG_PRINTF(
				"parse_dpni_max_fs_entries_per_tc() failed with error %d, cannot get max_fs_entries per RX traffic-class\n",
				error);
			return error;
		}
	} else {
		for (int i = 0; i < dpni_cfg.adv.max_tcs; ++i)
			dpni_extended_cfg.tc_cfg[i].max_fs_entries = 1;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAX_SENDERS)) {
		restool.cmd_option_mask &=
				~ONE_BIT_MASK(CREATE_OPT_MAX_SENDERS);
		error = get_option_value(CREATE_OPT_MAX_SENDERS, &val,
					 "Invalid max senders", 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_senders = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_senders = 1;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_UNICAST_FILTERS)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_UNICAST_FILTERS);
		error = get_option_value(CREATE_OPT_MAX_UNICAST_FILTERS, &val,
					 "Invalid max unicast filters", 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_unicast_filters = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_unicast_filters = 0;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_MULTICAST_FILTERS)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_MULTICAST_FILTERS);
		error = get_option_value(CREATE_OPT_MAX_MULTICAST_FILTERS, &val,
					 "Invalid max multicast filters",
					 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_multicast_filters = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_multicast_filters = 0;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_VLAN_FILTERS)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_VLAN_FILTERS);
		error = get_option_value(CREATE_OPT_MAX_VLAN_FILTERS, &val,
					 "Invalid max vlan filters",
					 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_vlan_filters = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_vlan_filters = 0;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_QOS_ENTRIES)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_QOS_ENTRIES);
		error = get_option_value(CREATE_OPT_MAX_QOS_ENTRIES, &val,
					 "Invalid max qos", 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_qos_entries = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_qos_entries = 0;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_QOS_KEY_SIZE)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_QOS_KEY_SIZE);
		error = get_option_value(CREATE_OPT_MAX_QOS_KEY_SIZE, &val,
					 "Invalid max qos key size", 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_qos_key_size = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_qos_key_size = 0;
	}

	if (restool.cmd_option_mask &
	    ONE_BIT_MASK(CREATE_OPT_MAX_DIST_KEY_SIZE)) {
		restool.cmd_option_mask &=
			~ONE_BIT_MASK(CREATE_OPT_MAX_DIST_KEY_SIZE);
		error = get_option_value(CREATE_OPT_MAX_DIST_KEY_SIZE, &val,
					 "Invalid max dist key size",
					 0, UINT8_MAX);
		if (error)
			return error;
		dpni_cfg.adv.max_dist_key_size = (uint8_t)val;
	} else {
		dpni_cfg.adv.max_dist_key_size = 0;
	}

	/**
	 * hack to get get 0.8.x flibs to work with mc
	 */
	error = dpni_create_v9(&restool.mc_io, 0, &dpni_cfg, &dpni_extended_cfg,
			       &dpni_handle);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			mc_status_to_string(mc_status), mc_status);
		return error;
	}


	memset(&dpni_attr, 0, sizeof(struct dpni_attr_v9));
	error = dpni_get_attributes_v9(&restool.mc_io, 0, dpni_handle,
				       &dpni_attr, &dpni_extended_cfg);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		return error;
	}
	print_new_obj("dpni", dpni_attr.id, NULL);

	error = dpni_close(&restool.mc_io, 0, dpni_handle);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		return error;
	}
	return 0;
}

static int cmd_dpni_create_v9(void)
{
	static const char usage_msg[] =
		"\n"
		"Usage: restool dpni create --mac-addr=<addr> [OPTIONS]\n"
		"   --mac-addr=<addr>\n"
		"	String specifying primary MAC address (e.g. 00:00:05:00:00:05).\n"
		"\n"
		"OPTIONS:\n"
		"--max-senders=<number>\n"
		"	maximum number of different senders;\n"
		"	will be used as the number of dedicated TX flows;\n"
		"	If it is not a power of two it will be rounded up to the next\n"
		"	power of two value.\n"
		"--options=<options-mask>\n"
		"   Where <options-mask> is a comma or space separated list of DPNI options:\n"
		"	DPNI_OPT_ALLOW_DIST_KEY_PER_TC\n"
		"	DPNI_OPT_TX_CONF_DISABLED\n"
		"	DPNI_OPT_PRIVATE_TX_CONF_ERROR_DISABLED\n"
		"	DPNI_OPT_DIST_HASH\n"
		"	DPNI_OPT_DIST_FS\n"
		"	DPNI_OPT_UNICAST_FILTER\n"
		"	DPNI_OPT_MULTICAST_FILTER\n"
		"	DPNI_OPT_VLAN_FILTER\n"
		"	DPNI_OPT_QOS_MASK_SUPPORT\n"
		"	DPNI_OPT_FS_MASK_SUPPORT\n"
		"--max-tcs=<number>\n"
		"	Specifies the maximum number of traffic-classes\n"
		"	0 will be treated as 1\n"
		"--max-dist-per-tc=<dist-size>,<dist-size>,...\n"
		"	Comma separated list of counts specifying the\n"
		"	maximum distribution's size per RX traffic-class\n"
		"--max-fs-entries-per-tc=<fs-entries>,<fs-entries>,...\n"
		"	Comma separated list of counts specifying the\n"
		"	maximum fs-entries per RX traffic-class\n"
		"--max-unicast-filters=<number>\n"
		"	maximum number of unicast filters (0 will be treated as 16)\n"
		"--max-multicast-filters=<number>\n"
		"	maximum number of multicast filters (0 will be treated as 64)\n"
		"--max-vlan-filters=<number>\n"
		"	maximum number of vlan filters (0 will be treated as 16)\n"
		"--max-qos-entries=<number>\n"
		"	if max_tcs > 1, declares the maximum entries for\n"
		"	the QoS table; '0' will be treated as '64'\n"
		"--max-qos-key-size=<number>\n"
		"	maximum key size for the QoS look-up;\n"
		"	'0' will be treated as '24' which enough for IPv4 5-tuple\n"
		"--max-dist-key-size=<number>\n"
		"	maximum key size for the distribution;\n"
		"	'0' will be treated as '24' which enough for IPv4 5-tuple\n"
		"\n"
		"EXAMPLE:\n"
		"Create a DPNI object with all default options:\n"
		"   $ restool dpni create --mac-addr=<addr>\n"
		"\n";

	return create_dpni_v9(usage_msg);
}

static int create_dpni_v10(const char *usage_msg)
{
	struct dpni_cfg_v10 dpni_cfg;
	uint32_t dpni_id, dprc_id;
	uint16_t dprc_handle;
	bool dprc_opened;
	long value;
	int error;

	memset(&dpni_cfg, 0, sizeof(dpni_cfg));

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_HELP)) {
		puts(usage_msg);
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_HELP);
		return 0;
	}

	if (restool.obj_name != NULL) {
		ERROR_PRINTF("Unexpected argument: \'%s\'\n\n",
			     restool.obj_name);
		puts(usage_msg);
		return -EINVAL;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAC_ENTRIES) &&
	    restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAC_FILTER_ENTRIES)) {
		ERROR_PRINTF("Please only use on of the following options " \
			     "since they are interchangeable:" \
			     " --mac-entries, --mac-filter-entries.\n");
		return -EINVAL;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_VLAN_ENTRIES) &&
	    restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_VLAN_FILTER_ENTRIES)) {
		ERROR_PRINTF("Please only use on of the following options " \
			     "since they are interchangeable:" \
			     " --vlan-entries, --vlan-filter-entries.\n");
		return -EINVAL;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_OPTIONS)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_OPTIONS);

		if (restool.mc_fw_version.minor == 0)
			error = parse_generic_create_options(
					restool.cmd_option_args[CREATE_OPT_OPTIONS],
					(uint64_t *)&dpni_cfg.options,
					options_map_v10_0,
					options_num_v10_0);
		else
			error = parse_generic_create_options(
					restool.cmd_option_args[CREATE_OPT_OPTIONS],
					(uint64_t *)&dpni_cfg.options,
					options_map_v10_1,
					options_num_v10_1);

		if (error) {
			DEBUG_PRINTF("parse_generic_create_options() = %d\n",
				     error);
			return error;
		}
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_NUM_QUEUES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_NUM_QUEUES);
		error = get_option_value(CREATE_OPT_NUM_QUEUES, &value,
				     "Invalid num-queues value\n", 1, 32);
		if (error)
			return error;
		dpni_cfg.num_queues = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_NUM_CHANNELS)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_NUM_CHANNELS);
		error = get_option_value(CREATE_OPT_NUM_CHANNELS, &value,
				     "Invalid num-channels value\n", 1, 32);
		if (error)
			return error;

		dpni_cfg.num_ceetm_ch = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_NUM_TCS)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_NUM_TCS);
		error = get_option_value(CREATE_OPT_NUM_TCS, &value,
				     "Invalid num-tcs value\n", 1, 16);
		if (error)
			return error;
		dpni_cfg.num_tcs = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_NUM_OPR)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_NUM_OPR);
		error = get_option_value(CREATE_OPT_NUM_OPR, &value,
					"Invalid num-opr value\n", 1,
					DPNI_MAX_OPR);
		if (error)
			return error;

		dpni_cfg.num_opr = (uint16_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAC_ENTRIES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_MAC_ENTRIES);
		error = get_option_value(CREATE_OPT_MAC_ENTRIES, &value,
				     "Invalid mac-entries value\n", 1, 80);
		if (error)
			return error;
		dpni_cfg.mac_filter_entries = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_MAC_FILTER_ENTRIES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_MAC_FILTER_ENTRIES);
		error = get_option_value(CREATE_OPT_MAC_FILTER_ENTRIES, &value,
				     "Invalid mac-filter-entries value\n", 1, 80);
		if (error)
			return error;
		dpni_cfg.mac_filter_entries = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_VLAN_ENTRIES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_VLAN_ENTRIES);
		error = get_option_value(CREATE_OPT_VLAN_ENTRIES, &value,
				     "Invalid vlan-entries value\n", 1, 16);
		if (error)
			return error;
		dpni_cfg.vlan_filter_entries = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_VLAN_FILTER_ENTRIES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_VLAN_FILTER_ENTRIES);
		error = get_option_value(CREATE_OPT_VLAN_FILTER_ENTRIES, &value,
				     "Invalid vlan-filter-entries value\n", 1, 16);
		if (error)
			return error;
		dpni_cfg.vlan_filter_entries = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_QOS_ENTRIES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_QOS_ENTRIES);
		error = get_option_value(CREATE_OPT_QOS_ENTRIES, &value,
				     "Invalid qos-entries value\n", 1, 64);
		if (error)
			return error;
		dpni_cfg.qos_entries = (uint8_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_FS_ENTRIES)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_FS_ENTRIES);
		error = get_option_value(CREATE_OPT_FS_ENTRIES, &value,
				     "Invalid fs-entries value\n", 1, 1024);
		if (error)
			return error;
		dpni_cfg.fs_entries = (uint16_t)value;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_NUM_CGS)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_NUM_CGS);
		error = get_option_value(CREATE_OPT_NUM_CGS, &value,
				     "Invalid num-cgs value\n", 1, 128);
		if (error)
			return error;
		dpni_cfg.num_cgs = (uint8_t)value;
	}

	dprc_handle = restool.root_dprc_handle;
	dprc_opened = false;
	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_PARENT_DPRC)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_PARENT_DPRC);
		error = parse_object_name(
				restool.cmd_option_args[CREATE_OPT_PARENT_DPRC],
				"dprc", &dprc_id);
		if (error)
			return error;

		if (restool.root_dprc_id != dprc_id) {
			error = open_dprc(dprc_id, &dprc_handle);
			if (error)
				return error;
			dprc_opened = true;
		}
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_DIST_KEY_SIZE)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_DIST_KEY_SIZE);
		error = get_option_value(CREATE_OPT_DIST_KEY_SIZE, &value,
				"Invalid dist-key-size value", 1, 56);
		if (error)
			return error;
		dpni_cfg.dist_key_size = (uint8_t)value;
	}

	error = dpni_create_v10(&restool.mc_io, dprc_handle, 0,
				&dpni_cfg, &dpni_id);
	if (error) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			mc_status_to_string(mc_status), mc_status);
		return error;
	}

	if (dprc_opened) {
		(void)dprc_close(&restool.mc_io, 0, dprc_handle);
		print_new_obj("dpni", dpni_id,
			      restool.cmd_option_args[CREATE_OPT_PARENT_DPRC]);
	} else {
		print_new_obj("dpni", dpni_id, NULL);
	}

	return 0;
}
static int cmd_dpni_create_v10(void)
{
	static const char usage_msg_v10_0[] =
		"\n"
		"Usage: restool dpni create [OPTIONS]\n"
		"\n"
		"OPTIONS:\n"
		"--options=<options-mask>\n"
		"   Where <options-mask> is a comma or space separated list of DPNI options:\n"
		"	DPNI_OPT_TX_FRM_RELEASE\n"
		"	DPNI_OPT_NO_MAC_FILTER\n"
		"	DPNI_OPT_HAS_POLICING\n"
		"	DPNI_OPT_SHARED_CONGESTION\n"
		"	DPNI_OPT_HAS_KEY_MASKING\n"
		"	DPNI_OPT_NO_FS\n"
		"--num-queues=<number>\n"
		"   Number of TX/RX queues use for traffic distribution.\n"
		"   Used to distribute traffic to multiple GPP cores,\n"
		"   Defaults to one queue. Maximim supported value is 32\n"
		"--num-tcs=<number>\n"
		"   Number of traffic classes (TCs), reserved for the DPNI.\n"
		"   Defaults to one TC. Maximum supported value is 8\n"
		"--mac-filter-entries=<number>\n"
		"   Number of entries in the MAC address filtering table.\n"
		"   Allows both unicast and multicast entries.\n"
		"   By default, there are 80 entries.Maximum supported value is 80.\n"
		"   This option is the former --mac-entries which can still be used.\n"
		"   The change was made to align with the options passed through DPL.\n"
		"--vlan-filter-entries=<number>\n"
		"   Number of entries in the VLAN address filtering table\n"
		"   By default, VLAN filtering is disabled. Maximum values is 16\n"
		"   This option is the former --vlan-entries which can still be used.\n"
		"   The change was made to align with the options passed through DPL.\n"
		"--qos-entries=<number>\n"
		"   Number of entries in the QoS classification table.\n"
		"   Ignored of DPNI has a single TC. By default, set to 64.\n"
		"--fs-entries=<number>\n"
		"   Number of entries in the flow steering table.\n"
		"   Defaults to 64. Maximum value is 1024\n"
		"--container=<container-name>\n"
		"   Specifies the parent container name. e.g. dprc.2, dprc.3 etc.\n"
		"\n";

	static const char usage_msg_v10_1[] =
		"\n"
		"Usage: restool dpni create [OPTIONS]\n"
		"\n"
		"OPTIONS:\n"
		"--options=<options-mask>\n"
		"   Where <options-mask> is a comma or space separated list of DPNI options:\n"
		"	DPNI_OPT_TX_FRM_RELEASE\n"
		"	DPNI_OPT_NO_MAC_FILTER\n"
		"	DPNI_OPT_HAS_POLICING\n"
		"	DPNI_OPT_SHARED_CONGESTION\n"
		"	DPNI_OPT_HAS_KEY_MASKING\n"
		"	DPNI_OPT_NO_FS\n"
		"	DPNI_OPT_HAS_OPR\n"
		"	DPNI_OPT_OPR_PER_TC\n"
		"	DPNI_OPT_SINGLE_SENDER\n"
		"	DPNI_OPT_CUSTOM_CG\n"
		"	DPNI_OPT_CUSTOM_OPR\n"
		"	DPNI_OPT_SHARED_HASH_KEY\n"
		"	DPNI_OPT_SHARED_FS\n"
		"--num-queues=<number>\n"
		"   Number of TX/RX queues use for traffic distribution.\n"
		"   Used to distribute traffic to multiple GPP cores,\n"
		"   Defaults to one queue. Maximim supported value is 32\n"
		"--num-tcs=<number>\n"
		"   Number of traffic classes (TCs), reserved for the DPNI.\n"
		"   Defaults to one TC. Maximum supported value is 16.\n"
		"   There are maximum 16 TCs for Tx and 8 TCs for Rx.\n"
		"   When num_tcs > 8, Tx will use this value but Rx will have\n"
		"   only 8 traffic classes.\n"
		"--mac-filter-entries=<number>\n"
		"   Number of entries in the MAC address filtering table.\n"
		"   Allows both unicast and multicast entries.\n"
		"   By default, there are 80 entries.Maximum supported value is 80.\n"
		"   This option is the former --mac-entries which can still be used.\n"
		"   The change was made to align with the options passed through DPL.\n"
		"--vlan-filter-entries=<number>\n"
		"   Number of entries in the VLAN address filtering table\n"
		"   By default, VLAN filtering is disabled. Maximum values is 16\n"
		"   This option is the former --vlan-entries which can still be used.\n"
		"   The change was made to align with the options passed through DPL.\n"
		"--qos-entries=<number>\n"
		"   Number of entries in the QoS classification table.\n"
		"   Ignored of DPNI has a single TC. By default, set to 64.\n"
		"--fs-entries=<number>\n"
		"   Number of entries in the flow steering table.\n"
		"   Defaults to 64. Maximum value is 1024\n"
		"--num-cgs=<number>\n"
		"   Number of congestion groups (CGs), reserved for the DPNI.\n"
		"   Defaults to one per TC. Maximum supported value is 128.\n"
		"--container=<container-name>\n"
		"   Specifies the parent container name. e.g. dprc.2, dprc.3 etc.\n"
		"--dist-key-size=<number>\n"
		"   maximum key size for the distribution;\n"
		"   '0' will be treated as '24' which enough for IPv4 5-tuple\n"
		"--num-channels=<number>\n"
		"   Number of egress channels used by this dpni object.\n"
		"   If not specified the dpni object will use a single CEETM channel\n"
		"--num-opr=<number>\n"
		"   Number of desired custom number of order point records when\n"
		"   DPNI_OPT_CUSTOM_OPR is set.\n"
		"   Maximum supported value is num_tcs * num_queues.\n"
		"   Defaults to num_tcs * num_queues.\n"
		"\n";

	if (restool.mc_fw_version.minor == 0)
		return create_dpni_v10(usage_msg_v10_0);
	else
		return create_dpni_v10(usage_msg_v10_1);
	return -EINVAL;
}

static int destroy_dpni_v9(uint32_t dpni_id)
{
	bool dpni_opened = false;
	uint16_t dpni_handle;
	int error, error2;

	error = dpni_open(&restool.mc_io, 0, dpni_id, &dpni_handle);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}
	dpni_opened = true;
	if (0 == dpni_handle) {
		DEBUG_PRINTF(
			"dpni_open() returned invalid handle (auth 0) for dpni.%u\n",
			dpni_id);
		error = -ENOENT;
		goto out;
	}

	error = dpni_destroy(&restool.mc_io, 0, dpni_handle);
	if (error < 0) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			     mc_status_to_string(mc_status), mc_status);
		goto out;
	}
	dpni_opened = false;
	printf("dpni.%u is destroyed\n", dpni_id);

out:
	if (dpni_opened) {
		error2 = dpni_close(&restool.mc_io, 0, dpni_handle);
		if (error2 < 0) {
			mc_status = flib_error_to_mc_status(error2);
			ERROR_PRINTF("MC error: %s (status %#x)\n",
				     mc_status_to_string(mc_status), mc_status);
			if (error == 0)
				error = error2;
		}
	}

	return error;
}

static int destroy_dpni_v10(uint32_t dpni_id)
{
	uint16_t dprc_handle;
	uint32_t dprc_id;
	int error;

	dprc_handle = restool.root_dprc_handle;
	dprc_id = restool.root_dprc_id;
	error = get_parent_dprc_id(dpni_id, "dpni", &dprc_id);
	if (error)
		return error;

	if (dprc_id != restool.root_dprc_id) {
		error = open_dprc(dprc_id, &dprc_handle);
		if (error) {
			mc_status = flib_error_to_mc_status(error);
			ERROR_PRINTF("MC error: %s (status %#x)\n",
				mc_status_to_string(mc_status), mc_status);
			return error;
		}
	}

	error = dpni_destroy_v10(&restool.mc_io, dprc_handle,
				 0, dpni_id);
	if (error) {
		mc_status = flib_error_to_mc_status(error);
		ERROR_PRINTF("MC error: %s (status %#x)\n",
			mc_status_to_string(mc_status), mc_status);
		goto out;
	}
	printf("dpni.%u is destroyed\n", dpni_id);

out:
	if (dprc_id != restool.root_dprc_id) {
		error = dprc_close(&restool.mc_io, 0, dprc_handle);
		if (error) {
			mc_status = flib_error_to_mc_status(error);
			ERROR_PRINTF("MC error: %s (status %#x)\n",
				mc_status_to_string(mc_status), mc_status);
		}
	}

	return error;
}

static int destroy_dpni(int mc_fw_version)
{
	static const char usage_msg[] =
		"\n"
		"Usage: restool dpni destroy <dpni-object>\n"
		"   e.g. restool dpni destroy dpni.9\n"
		"\n";

	int error;
	uint32_t dpni_id;

	if (restool.cmd_option_mask & ONE_BIT_MASK(DESTROY_OPT_HELP)) {
		puts(usage_msg);
		restool.cmd_option_mask &= ~ONE_BIT_MASK(DESTROY_OPT_HELP);
		return 0;
	}

	if (restool.obj_name == NULL) {
		ERROR_PRINTF("<object> argument missing\n");
		puts(usage_msg);
		error = -EINVAL;
		goto out;
	}

	if (in_use(restool.obj_name, "destroyed")) {
		error = -EBUSY;
		goto out;
	}

	error = parse_object_name(restool.obj_name, "dpni", &dpni_id);
	if (error < 0)
		goto out;

	if (!find_obj("dpni", dpni_id)) {
		error = -EINVAL;
		goto out;
	}

	if (mc_fw_version == MC_FW_VERSION_9)
		error = destroy_dpni_v9(dpni_id);
	else if (mc_fw_version == MC_FW_VERSION_10)
		error = destroy_dpni_v10(dpni_id);
	else
		return -EINVAL;
out:
	return error;
}

static int cmd_dpni_destroy_v9(void)
{
	return destroy_dpni(MC_FW_VERSION_9);
}

static int cmd_dpni_destroy_v10(void)
{
	return destroy_dpni(MC_FW_VERSION_10);
}

static int update_dpni_v10(const char *usage_msg)
{
	uint8_t mac_addr[6];
	uint16_t dpni_handle;
	uint32_t dpni_id;
	int error = 0;
	bool minimal_options = false;

	if (restool.cmd_option_mask & ONE_BIT_MASK(CREATE_OPT_HELP)) {
		puts(usage_msg);
		restool.cmd_option_mask &= ~ONE_BIT_MASK(CREATE_OPT_HELP);
		return 0;
	}

	if (restool.obj_name == NULL) {
		ERROR_PRINTF("Should provide a dpni!\n");
		puts(usage_msg);
		return -EINVAL;
	}

	error = parse_object_name(restool.obj_name, "dpni", &dpni_id);
	if (error) {
		puts(usage_msg);
		goto out;
	}

	error = dpni_open_v10(&restool.mc_io, 0, dpni_id, &dpni_handle);
	if (error) {
		ERROR_PRINTF("Could not open specified object!\n");
		puts(usage_msg);
		goto out;
	}

	if (restool.cmd_option_mask & ONE_BIT_MASK(UPDATE_MAC_ADDR)) {
		restool.cmd_option_mask &= ~ONE_BIT_MASK(UPDATE_MAC_ADDR);
		error  = parse_dpni_mac_addr(
				restool.cmd_option_args[UPDATE_MAC_ADDR],
				mac_addr);

		error = dpni_set_primary_mac_addr_v10(&restool.mc_io, 0,
						      dpni_handle, mac_addr);
		if (error)
			ERROR_PRINTF("dpni_set_primary_mac_addr_v10() = %d\n", error);

		minimal_options = true;
	}

	dpni_close_v10(&restool.mc_io, 0, dpni_handle);
out:

	if (!minimal_options && !error) {
		ERROR_PRINTF("Specify at least an option to update!\n");
		puts(usage_msg);
	}

	return error;
}


static int cmd_dpni_update_v10(void)
{
	static const char usage_msg[] =
		"\n"
		"Usage: restool dpni update dpni.X [OPTIONS]\n"
		"\n"
		"OPTIONS:\n"
		"   --mac-addr=<addr>\n"
		"	String specifying primary MAC address (e.g. 00:00:05:00:00:05).\n"
		"\n";

	return update_dpni_v10(usage_msg);
}

struct object_command dpni_commands_v9[] = {
	{ .cmd_name = "--help",
	  .options = NULL,
	  .cmd_func = cmd_dpni_help },

	{ .cmd_name = "info",
	  .options = dpni_info_options,
	  .cmd_func = cmd_dpni_info_v9 },

	{ .cmd_name = "create",
	  .options = dpni_create_options,
	  .cmd_func = cmd_dpni_create_v9 },

	{ .cmd_name = "destroy",
	  .options = dpni_destroy_options,
	  .cmd_func = cmd_dpni_destroy_v9 },

	{ .cmd_name = NULL },
};

struct object_command dpni_commands_v10[] = {
	{ .cmd_name = "--help",
	  .options = NULL,
	  .cmd_func = cmd_dpni_help_v10 },

	{ .cmd_name = "info",
	  .options = dpni_info_options,
	  .cmd_func = cmd_dpni_info_v10 },

	{ .cmd_name = "create",
	  .options = dpni_create_options,
	  .cmd_func = cmd_dpni_create_v10 },

	{ .cmd_name = "destroy",
	  .options = dpni_destroy_options,
	  .cmd_func = cmd_dpni_destroy_v10 },

	{ .cmd_name = "update",
	  .options = dpni_update_options_v10,
	  .cmd_func = cmd_dpni_update_v10 },

	{ .cmd_name = NULL },
};

