/*
 * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
 * Copyright (c) 2007 Xsigo Systems Inc.  All rights reserved.
 * Copyright (c) 2009-2011 Mellanox Technologies LTD.  All rights reserved.
 * Copyright (c) 2009 HNR Consulting.  All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     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.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <infiniband/mad.h>

void mad_dump_int(char *buf, int bufsz, void *val, int valsz)
{
	switch (valsz) {
	case 1:
		snprintf(buf, bufsz, "%d", *(uint32_t *) val & 0xff);
		break;
	case 2:
		snprintf(buf, bufsz, "%d", *(uint32_t *) val & 0xffff);
		break;
	case 3:
	case 4:
		snprintf(buf, bufsz, "%d", *(uint32_t *) val);
		break;
	case 5:
	case 6:
	case 7:
	case 8:
		snprintf(buf, bufsz, "%" PRIu64, *(uint64_t *) val);
		break;
	default:
		IBWARN("bad int sz %d", valsz);
		buf[0] = 0;
	}
}

void mad_dump_uint(char *buf, int bufsz, void *val, int valsz)
{
	switch (valsz) {
	case 1:
		snprintf(buf, bufsz, "%u", *(uint32_t *) val & 0xff);
		break;
	case 2:
		snprintf(buf, bufsz, "%u", *(uint32_t *) val & 0xffff);
		break;
	case 3:
	case 4:
		snprintf(buf, bufsz, "%u", *(uint32_t *) val);
		break;
	case 5:
	case 6:
	case 7:
	case 8:
		snprintf(buf, bufsz, "%" PRIu64, *(uint64_t *) val);
		break;
	default:
		IBWARN("bad int sz %u", valsz);
		buf[0] = 0;
	}
}

void mad_dump_hex(char *buf, int bufsz, void *val, int valsz)
{
	switch (valsz) {
	case 1:
		snprintf(buf, bufsz, "0x%02x", *(uint32_t *) val & 0xff);
		break;
	case 2:
		snprintf(buf, bufsz, "0x%04x", *(uint32_t *) val & 0xffff);
		break;
	case 3:
		snprintf(buf, bufsz, "0x%06x", *(uint32_t *) val & 0xffffff);
		break;
	case 4:
		snprintf(buf, bufsz, "0x%08x", *(uint32_t *) val);
		break;
	case 5:
		snprintf(buf, bufsz, "0x%010" PRIx64,
			 *(uint64_t *) val & (uint64_t) 0xffffffffffULL);
		break;
	case 6:
		snprintf(buf, bufsz, "0x%012" PRIx64,
			 *(uint64_t *) val & (uint64_t) 0xffffffffffffULL);
		break;
	case 7:
		snprintf(buf, bufsz, "0x%014" PRIx64,
			 *(uint64_t *) val & (uint64_t) 0xffffffffffffffULL);
		break;
	case 8:
		snprintf(buf, bufsz, "0x%016" PRIx64, *(uint64_t *) val);
		break;
	default:
		IBWARN("bad int sz %d", valsz);
		buf[0] = 0;
	}
}

void mad_dump_rhex(char *buf, int bufsz, void *val, int valsz)
{
	switch (valsz) {
	case 1:
		snprintf(buf, bufsz, "%02x", *(uint32_t *) val & 0xff);
		break;
	case 2:
		snprintf(buf, bufsz, "%04x", *(uint32_t *) val & 0xffff);
		break;
	case 3:
		snprintf(buf, bufsz, "%06x", *(uint32_t *) val & 0xffffff);
		break;
	case 4:
		snprintf(buf, bufsz, "%08x", *(uint32_t *) val);
		break;
	case 5:
		snprintf(buf, bufsz, "%010" PRIx64,
			 *(uint64_t *) val & (uint64_t) 0xffffffffffULL);
		break;
	case 6:
		snprintf(buf, bufsz, "%012" PRIx64,
			 *(uint64_t *) val & (uint64_t) 0xffffffffffffULL);
		break;
	case 7:
		snprintf(buf, bufsz, "%014" PRIx64,
			 *(uint64_t *) val & (uint64_t) 0xffffffffffffffULL);
		break;
	case 8:
		snprintf(buf, bufsz, "%016" PRIx64, *(uint64_t *) val);
		break;
	default:
		IBWARN("bad int sz %d", valsz);
		buf[0] = 0;
	}
}

void mad_dump_linkwidth(char *buf, int bufsz, void *val, int valsz)
{
	int width = *(int *)val;

	switch (width) {
	case 1:
		snprintf(buf, bufsz, "1X");
		break;
	case 2:
		snprintf(buf, bufsz, "4X");
		break;
	case 4:
		snprintf(buf, bufsz, "8X");
		break;
	case 8:
		snprintf(buf, bufsz, "12X");
		break;
	case 16:
		snprintf(buf, bufsz, "2X");
		break;
	default:
		IBWARN("bad width %d", width);
		snprintf(buf, bufsz, "undefined (%d)", width);
		break;
	}
}

static void dump_linkwidth(char *buf, int bufsz, int width)
{
	int n = 0;

	if (width & 0x1)
		n += snprintf(buf + n, bufsz - n, "1X or ");
	if (n < bufsz && (width & 0x2))
		n += snprintf(buf + n, bufsz - n, "4X or ");
	if (n < bufsz && (width & 0x4))
		n += snprintf(buf + n, bufsz - n, "8X or ");
	if (n < bufsz && (width & 0x8))
		n += snprintf(buf + n, bufsz - n, "12X or ");
	if (n < bufsz && (width & 0x10))
		n += snprintf(buf + n, bufsz - n, "2X or ");

	if (n >= bufsz)
		return;
	else if (width == 0 || (width >> 5))
		snprintf(buf + n, bufsz - n, "undefined (%d)", width);
	else if (bufsz > 3)
		buf[n - 4] = '\0';
}

void mad_dump_linkwidthsup(char *buf, int bufsz, void *val, int valsz)
{
	int width = *(int *)val;

	dump_linkwidth(buf, bufsz, width);

	switch (width) {
	case 1:
	case 3:
	case 7:
	case 11:
	case 15:
	case 17:
	case 19:
	case 23:
	case 27:
	case 31:
		break;

	default:
		if (!(width >> 5))
			snprintf(buf + strlen(buf), bufsz - strlen(buf),
				 " (IBA extension)");
		break;
	}
}

void mad_dump_linkwidthen(char *buf, int bufsz, void *val, int valsz)
{
	int width = *(int *)val;

	dump_linkwidth(buf, bufsz, width);
}

void mad_dump_linkspeed(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *)val;

	switch (speed) {
	case 0:
		snprintf(buf, bufsz, "Extended speed");
		break;
	case 1:
		snprintf(buf, bufsz, "2.5 Gbps");
		break;
	case 2:
		snprintf(buf, bufsz, "5.0 Gbps");
		break;
	case 4:
		snprintf(buf, bufsz, "10.0 Gbps");
		break;
	default:
		snprintf(buf, bufsz, "undefined (%d)", speed);
		break;
	}
}

static void dump_linkspeed(char *buf, int bufsz, int speed)
{
	int n = 0;

	if (speed & 0x1)
		n += snprintf(buf + n, bufsz - n, "2.5 Gbps or ");
	if (n < bufsz && (speed & 0x2))
		n += snprintf(buf + n, bufsz - n, "5.0 Gbps or ");
	if (n < bufsz && (speed & 0x4))
		n += snprintf(buf + n, bufsz - n, "10.0 Gbps or ");

	if (n >= bufsz)
		return;
	else if (speed == 0 || (speed >> 3)) {
		n += snprintf(buf + n, bufsz - n, "undefined (%d)", speed);
		if (n >= bufsz)
			return;
	} else if (bufsz > 3) {
		buf[n - 4] = '\0';
		n -= 4;
	}

	switch (speed) {
	case 1:
	case 3:
	case 5:
	case 7:
		break;
	default:
		if (!(speed >> 3))
			snprintf(buf + n, bufsz - n, " (IBA extension)");
		break;
	}
}

void mad_dump_linkspeedsup(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *)val;

	dump_linkspeed(buf, bufsz, speed);
}

void mad_dump_linkspeeden(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *)val;

	dump_linkspeed(buf, bufsz, speed);
}

void mad_dump_linkspeedext(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *)val;

	switch (speed) {
	case 0:
		snprintf(buf, bufsz, "No Extended Speed");
		break;
	case 1:
		snprintf(buf, bufsz, "14.0625 Gbps");
		break;
	case 2:
		snprintf(buf, bufsz, "25.78125 Gbps");
		break;
	case 4:
		snprintf(buf, bufsz, "53.125 Gbps");
		break;
	case 8:
		snprintf(buf, bufsz, "106.25 Gbps");
		break;
	default:
		snprintf(buf, bufsz, "undefined (%d)", speed);
		break;
	}
}

static void dump_linkspeedext(char *buf, int bufsz, int speed)
{
	int n = 0;

	if (speed == 0) {
		sprintf(buf, "%d", speed);
		return;
	}

	if (speed & 0x1)
		n += snprintf(buf + n, bufsz - n, "14.0625 Gbps or ");
	if (n < bufsz && speed & 0x2)
		n += snprintf(buf + n, bufsz - n, "25.78125 Gbps or ");
	if (n < bufsz && speed & 0x4)
		n += snprintf(buf + n, bufsz - n, "53.125 Gbps or ");
	if (n < bufsz && speed & 0x8)
		n += snprintf(buf + n, bufsz - n, "106.25 Gbps or ");

	if (n >= bufsz) {
		if (bufsz > 3)
			buf[n - 4] = '\0';
		return;
	}

	if (speed >> 4) {
		n += snprintf(buf + n, bufsz - n, "undefined (%d)", speed);
		return;
	} else if (bufsz > 3)
		buf[n - 4] = '\0';
}

void mad_dump_linkspeedextsup(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *)val;

	dump_linkspeedext(buf, bufsz, speed);
}

void mad_dump_linkspeedexten(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *)val;

	if (speed == 30) {
		sprintf(buf, "%s", "Extended link speeds disabled");
		return;
	}
	dump_linkspeedext(buf, bufsz, speed);
}

void mad_dump_linkspeedext2(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *) val;

	switch (speed) {
	case 0:
		snprintf(buf, bufsz, "No Extended Speed 2");
		break;
	case 1:
		snprintf(buf, bufsz, "212.5 Gbps");
		break;
	default:
		snprintf(buf, bufsz, "undefined (%d)", speed);
		break;
	}
}

static void dump_linkspeedext2(char *buf, int bufsz, int speed)
{
	int n = 0;

	if (speed == 0) {
		snprintf(buf, bufsz, "%d", speed);
		return;
	}

	if (speed & 0x1)
		snprintf(buf, bufsz, "212.5 Gbps");

	if (n >= bufsz)
		return;

	if (speed >> 1)
		snprintf(buf + n, bufsz - n, " undefined (%d)", speed);
}


void mad_dump_linkspeedextsup2(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *) val;

	dump_linkspeedext2(buf, bufsz, speed);
}

void mad_dump_linkspeedexten2(char *buf, int bufsz, void *val, int valsz)
{
	int speed = *(int *) val;

	dump_linkspeedext2(buf, bufsz, speed);
}


void mad_dump_portstate(char *buf, int bufsz, void *val, int valsz)
{
	int state = *(int *)val;

	switch (state) {
	case 0:
		snprintf(buf, bufsz, "NoChange");
		break;
	case 1:
		snprintf(buf, bufsz, "Down");
		break;
	case 2:
		snprintf(buf, bufsz, "Initialize");
		break;
	case 3:
		snprintf(buf, bufsz, "Armed");
		break;
	case 4:
		snprintf(buf, bufsz, "Active");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)", state);
	}
}

void mad_dump_linkdowndefstate(char *buf, int bufsz, void *val, int valsz)
{
	int state = *(int *)val;

	switch (state) {
	case 0:
		snprintf(buf, bufsz, "NoChange");
		break;
	case 1:
		snprintf(buf, bufsz, "Sleep");
		break;
	case 2:
		snprintf(buf, bufsz, "Polling");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)", state);
		break;
	}
}

void mad_dump_physportstate(char *buf, int bufsz, void *val, int valsz)
{
	int state = *(int *)val;

	switch (state) {
	case 0:
		snprintf(buf, bufsz, "NoChange");
		break;
	case 1:
		snprintf(buf, bufsz, "Sleep");
		break;
	case 2:
		snprintf(buf, bufsz, "Polling");
		break;
	case 3:
		snprintf(buf, bufsz, "Disabled");
		break;
	case 4:
		snprintf(buf, bufsz, "PortConfigurationTraining");
		break;
	case 5:
		snprintf(buf, bufsz, "LinkUp");
		break;
	case 6:
		snprintf(buf, bufsz, "LinkErrorRecovery");
		break;
	case 7:
		snprintf(buf, bufsz, "PhyTest");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)", state);
	}
}

void mad_dump_mtu(char *buf, int bufsz, void *val, int valsz)
{
	int mtu = *(int *)val;

	switch (mtu) {
	case 1:
		snprintf(buf, bufsz, "256");
		break;
	case 2:
		snprintf(buf, bufsz, "512");
		break;
	case 3:
		snprintf(buf, bufsz, "1024");
		break;
	case 4:
		snprintf(buf, bufsz, "2048");
		break;
	case 5:
		snprintf(buf, bufsz, "4096");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)", mtu);
	}
}

void mad_dump_vlcap(char *buf, int bufsz, void *val, int valsz)
{
	int vlcap = *(int *)val;

	switch (vlcap) {
	case 1:
		snprintf(buf, bufsz, "VL0");
		break;
	case 2:
		snprintf(buf, bufsz, "VL0-1");
		break;
	case 3:
		snprintf(buf, bufsz, "VL0-3");
		break;
	case 4:
		snprintf(buf, bufsz, "VL0-7");
		break;
	case 5:
		snprintf(buf, bufsz, "VL0-14");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)", vlcap);
	}
}

void mad_dump_opervls(char *buf, int bufsz, void *val, int valsz)
{
	int opervls = *(int *)val;

	switch (opervls) {
	case 0:
		snprintf(buf, bufsz, "No change");
		break;
	case 1:
		snprintf(buf, bufsz, "VL0");
		break;
	case 2:
		snprintf(buf, bufsz, "VL0-1");
		break;
	case 3:
		snprintf(buf, bufsz, "VL0-3");
		break;
	case 4:
		snprintf(buf, bufsz, "VL0-7");
		break;
	case 5:
		snprintf(buf, bufsz, "VL0-14");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)", opervls);
	}
}

void mad_dump_portcapmask(char *buf, int bufsz, void *val, int valsz)
{
	unsigned mask = *(unsigned *)val;
	char *s = buf;

	s += sprintf(s, "0x%x\n", mask);
	if (mask & (1 << 1))
		s += sprintf(s, "\t\t\t\tIsSM\n");
	if (mask & (1 << 2))
		s += sprintf(s, "\t\t\t\tIsNoticeSupported\n");
	if (mask & (1 << 3))
		s += sprintf(s, "\t\t\t\tIsTrapSupported\n");
	if (mask & (1 << 4))
		s += sprintf(s, "\t\t\t\tIsOptionalIPDSupported\n");
	if (mask & (1 << 5))
		s += sprintf(s, "\t\t\t\tIsAutomaticMigrationSupported\n");
	if (mask & (1 << 6))
		s += sprintf(s, "\t\t\t\tIsSLMappingSupported\n");
	if (mask & (1 << 7))
		s += sprintf(s, "\t\t\t\tIsMKeyNVRAM\n");
	if (mask & (1 << 8))
		s += sprintf(s, "\t\t\t\tIsPKeyNVRAM\n");
	if (mask & (1 << 9))
		s += sprintf(s, "\t\t\t\tIsLedInfoSupported\n");
	if (mask & (1 << 10))
		s += sprintf(s, "\t\t\t\tIsSMdisabled\n");
	if (mask & (1 << 11))
		s += sprintf(s, "\t\t\t\tIsSystemImageGUIDsupported\n");
	if (mask & (1 << 12))
		s += sprintf(s,
			     "\t\t\t\tIsPkeySwitchExternalPortTrapSupported\n");
	if (mask & (1 << 14))
		s += sprintf(s, "\t\t\t\tIsExtendedSpeedsSupported\n");
	if (mask & (1 << 15))
		s += sprintf(s, "\t\t\t\tIsCapabilityMask2Supported\n");
	if (mask & (1 << 16))
		s += sprintf(s, "\t\t\t\tIsCommunicatonManagementSupported\n");
	if (mask & (1 << 17))
		s += sprintf(s, "\t\t\t\tIsSNMPTunnelingSupported\n");
	if (mask & (1 << 18))
		s += sprintf(s, "\t\t\t\tIsReinitSupported\n");
	if (mask & (1 << 19))
		s += sprintf(s, "\t\t\t\tIsDeviceManagementSupported\n");
	if (mask & (1 << 20))
		s += sprintf(s, "\t\t\t\tIsVendorClassSupported\n");
	if (mask & (1 << 21))
		s += sprintf(s, "\t\t\t\tIsDRNoticeSupported\n");
	if (mask & (1 << 22))
		s += sprintf(s, "\t\t\t\tIsCapabilityMaskNoticeSupported\n");
	if (mask & (1 << 23))
		s += sprintf(s, "\t\t\t\tIsBootManagementSupported\n");
	if (mask & (1 << 24))
		s += sprintf(s, "\t\t\t\tIsLinkRoundTripLatencySupported\n");
	if (mask & (1 << 25))
		s += sprintf(s, "\t\t\t\tIsClientRegistrationSupported\n");
	if (mask & (1 << 26))
		s += sprintf(s, "\t\t\t\tIsOtherLocalChangesNoticeSupported\n");
	if (mask & (1 << 27))
		s += sprintf(s,
			     "\t\t\t\tIsLinkSpeedWidthPairsTableSupported\n");
	if (mask & (1 << 28))
		s += sprintf(s, "\t\t\t\tIsVendorSpecificMadsTableSupported\n");
	if (mask & (1 << 29))
		s += sprintf(s, "\t\t\t\tIsMcastPkeyTrapSuppressionSupported\n");
	if (mask & (1 << 30))
		s += sprintf(s, "\t\t\t\tIsMulticastFDBTopSupported\n");
	if (mask & ((1U) << 31))
		s += sprintf(s, "\t\t\t\tIsHierarchyInfoSupported\n");

	if (s != buf)
		*(--s) = 0;
}

void mad_dump_portcapmask2(char *buf, int bufsz, void *val, int valsz)
{
	int mask = *(int *)val;
	char *s = buf;

	s += sprintf(s, "0x%x\n", mask);
	if (mask & (1 << 0))
		s += sprintf(s, "\t\t\t\tIsSetNodeDescriptionSupported\n");
	if (mask & (1 << 1))
		s += sprintf(s, "\t\t\t\tIsPortInfoExtendedSupported\n");
	if (mask & (1 << 2))
		s += sprintf(s, "\t\t\t\tIsVirtualizationSupported\n");
	if (mask & (1 << 3))
		s += sprintf(s, "\t\t\t\tIsSwitchPortStateTableSupported\n");
	if (mask & (1 << 4))
		s += sprintf(s, "\t\t\t\tIsLinkWidth2xSupported\n");
	if (mask & (1 << 5))
		s += sprintf(s, "\t\t\t\tIsLinkSpeedHDRSupported\n");
	if (mask & (1 << 6))
		s += sprintf(s, "\t\t\t\tIsMKeyProtectBitsExtSupported\n");
	if (mask & (1 << 7))
		s += sprintf(s, "\t\t\t\tIsEnhancedTrap128Supported\n");
	if (mask & (1 << 8))
		s += sprintf(s, "\t\t\t\tIsPartitionTopSupported\n");
	if (mask & (1 << 9))
		s += sprintf(s, "\t\t\t\tIsEnhancedQoSArbiterSupported\n");
	if (mask & (1 << 10))
		s += sprintf(s, "\t\t\t\tIsLinkSpeedNDRSupported\n");

	if (s != buf)
		*(--s) = 0;
}

void mad_dump_bitfield(char *buf, int bufsz, void *val, int valsz)
{
	snprintf(buf, bufsz, "0x%x", *(uint32_t *) val);
}

void mad_dump_array(char *buf, int bufsz, void *val, int valsz)
{
	uint8_t *p = val, *e;
	char *s = buf;

	if (bufsz < valsz * 2)
		valsz = bufsz / 2;

	for (p = val, e = p + valsz; p < e; p++, s += 2)
		sprintf(s, "%02x", *p);
}

void mad_dump_string(char *buf, int bufsz, void *val, int valsz)
{
	if (bufsz < valsz)
		valsz = bufsz;

	snprintf(buf, valsz, "'%s'", (char *)val);
}

void mad_dump_node_type(char *buf, int bufsz, void *val, int valsz)
{
	int nodetype = *(int *)val;

	switch (nodetype) {
	case 1:
		snprintf(buf, bufsz, "Channel Adapter");
		break;
	case 2:
		snprintf(buf, bufsz, "Switch");
		break;
	case 3:
		snprintf(buf, bufsz, "Router");
		break;
	default:
		snprintf(buf, bufsz, "?(%d)?", nodetype);
		break;
	}
}

#define IB_MAX_NUM_VLS 16
#define IB_MAX_NUM_VLS_TO_U8 ((IB_MAX_NUM_VLS)/2)

typedef struct _ib_slvl_table {
	uint8_t vl_by_sl_num[IB_MAX_NUM_VLS_TO_U8];
} ib_slvl_table_t;

static inline void ib_slvl_get_i(ib_slvl_table_t * tbl, int i, uint8_t * vl)
{
	*vl = (tbl->vl_by_sl_num[i >> 1] >> ((!(i & 1)) << 2)) & 0xf;
}

#define IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK 32

typedef struct _ib_vl_arb_table {
	struct {
		uint8_t res_vl;
		uint8_t weight;
	} vl_entry[IB_NUM_VL_ARB_ELEMENTS_IN_BLOCK];
} ib_vl_arb_table_t;

static inline void ib_vl_arb_get_vl(uint8_t res_vl, uint8_t * const vl)
{
	*vl = res_vl & 0x0F;
}

void mad_dump_sltovl(char *buf, int bufsz, void *val, int valsz)
{
	ib_slvl_table_t *p_slvl_tbl = val;
	uint8_t vl;
	int i, n = 0;
	n = snprintf(buf, bufsz, "|");
	for (i = 0; i < 16; i++) {
		ib_slvl_get_i(p_slvl_tbl, i, &vl);
		n += snprintf(buf + n, bufsz - n, "%2u|", vl);
		if (n >= bufsz)
			break;
	}
	snprintf(buf + n, bufsz - n, "\n");
}

void mad_dump_vlarbitration(char *buf, int bufsz, void *val, int num)
{
	ib_vl_arb_table_t *p_vla_tbl = val;
	int i, n;
	uint8_t vl;

	num /= sizeof(p_vla_tbl->vl_entry[0]);

	n = snprintf(buf, bufsz, "\nVL    : |");
	if (n >= bufsz)
		return;
	for (i = 0; i < num; i++) {
		ib_vl_arb_get_vl(p_vla_tbl->vl_entry[i].res_vl, &vl);
		n += snprintf(buf + n, bufsz - n, "0x%-2X|", vl);
		if (n >= bufsz)
			return;
	}

	n += snprintf(buf + n, bufsz - n, "\nWEIGHT: |");
	if (n >= bufsz)
		return;
	for (i = 0; i < num; i++) {
		n += snprintf(buf + n, bufsz - n, "0x%-2X|",
			      p_vla_tbl->vl_entry[i].weight);
		if (n >= bufsz)
			return;
	}

	snprintf(buf + n, bufsz - n, "\n");
}

static int _dump_fields(char *buf, int bufsz, void *data, int start, int end)
{
	char val[64];
	char *s = buf;
	int n, field;

	for (field = start; field < end && bufsz > 0; field++) {
		mad_decode_field(data, field, val);
		if (!mad_dump_field(field, s, bufsz-1, val))
			return -1;
		n = strlen(s);
		s += n;
		*s++ = '\n';
		*s = 0;
		n++;
		bufsz -= n;
	}

	return (int)(s - buf);
}

void mad_dump_fields(char *buf, int bufsz, void *val, int valsz, int start,
		     int end)
{
	_dump_fields(buf, bufsz, val, start, end);
}

void mad_dump_nodedesc(char *buf, int bufsz, void *val, int valsz)
{
	strncpy(buf, val, bufsz);

	if (valsz < bufsz)
		buf[valsz] = 0;
}

void mad_dump_nodeinfo(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_NODE_FIRST_F, IB_NODE_LAST_F);
}

void mad_dump_portinfo(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PORT_FIRST_F, IB_PORT_LAST_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val,
		     IB_PORT_CAPMASK2_F, IB_PORT_LINK_SPEED_EXT_LAST_F);
}

void mad_dump_portstates(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_PORT_STATE_F, IB_PORT_LINK_DOWN_DEF_F);
}

void mad_dump_switchinfo(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_SW_FIRST_F, IB_SW_LAST_F);
}

void mad_dump_perfcounters(char *buf, int bufsz, void *val, int valsz)
{
	int cnt, cnt2;

	cnt = _dump_fields(buf, bufsz, val,
			   IB_PC_FIRST_F, IB_PC_VL15_DROPPED_F);
	if (cnt < 0)
		return;

	cnt2 = _dump_fields(buf + cnt, bufsz - cnt, val,
			    IB_PC_QP1_DROP_F, IB_PC_QP1_DROP_F + 1);
	if (cnt2 < 0)
		return;

	_dump_fields(buf + cnt + cnt2, bufsz - cnt - cnt2, val,
		     IB_PC_VL15_DROPPED_F, IB_PC_LAST_F);
}

void mad_dump_perfcounters_ext(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_FIRST_F, IB_PC_EXT_LAST_F);
	if (cnt < 0)
		return;

	 _dump_fields(buf + cnt, bufsz - cnt, val,
		      IB_PC_EXT_COUNTER_SELECT2_F, IB_PC_EXT_ERR_LAST_F);
}

void mad_dump_perfcounters_xmt_sl(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_XMT_DATA_SL_FIRST_F,
		     IB_PC_XMT_DATA_SL_LAST_F);
}

void mad_dump_perfcounters_rcv_sl(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_RCV_DATA_SL_FIRST_F,
		     IB_PC_RCV_DATA_SL_LAST_F);
}

void mad_dump_perfcounters_xmt_disc(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_XMT_INACT_DISC_F,
		     IB_PC_XMT_DISC_LAST_F);
}

void mad_dump_perfcounters_rcv_err(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_RCV_LOCAL_PHY_ERR_F,
		     IB_PC_RCV_ERR_LAST_F);
}

void mad_dump_portsamples_control(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_PSC_OPCODE_F, IB_PSC_LAST_F);
}

void mad_dump_portsamples_result(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_PSR_TAG_F, IB_PSR_LAST_F);
}

void mad_dump_port_ext_speeds_counters_rsfec_active(char *buf, int bufsz,
						    void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_PESC_RSFEC_FIRST_F,
		     IB_PESC_RSFEC_LAST_F);
}

void mad_dump_port_ext_speeds_counters(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_PESC_PORT_SELECT_F, IB_PESC_LAST_F);
}

void mad_dump_perfcounters_port_op_rcv_counters(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_PORT_OP_RCV_COUNTERS_FIRST_F,
		     IB_PC_PORT_OP_RCV_COUNTERS_LAST_F);
}

void mad_dump_perfcounters_port_flow_ctl_counters(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_PORT_FLOW_CTL_COUNTERS_FIRST_F,
		     IB_PC_PORT_FLOW_CTL_COUNTERS_LAST_F);
}

void mad_dump_perfcounters_port_vl_op_packet(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_PORT_VL_OP_PACKETS_FIRST_F,
		     IB_PC_PORT_VL_OP_PACKETS_LAST_F);
}

void mad_dump_perfcounters_port_vl_op_data(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_PORT_VL_OP_DATA_FIRST_F,
		     IB_PC_PORT_VL_OP_DATA_LAST_F);
}

void mad_dump_perfcounters_port_vl_xmit_flow_ctl_update_errors(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_PORT_VL_XMIT_FLOW_CTL_UPDATE_ERRORS_FIRST_F,
		     IB_PC_PORT_VL_XMIT_FLOW_CTL_UPDATE_ERRORS_LAST_F);
}

void mad_dump_perfcounters_port_vl_xmit_wait_counters(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_PORT_VL_XMIT_WAIT_COUNTERS_FIRST_F,
		     IB_PC_PORT_VL_XMIT_WAIT_COUNTERS_LAST_F);
}

void mad_dump_perfcounters_sw_port_vl_congestion(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_SW_PORT_VL_CONGESTION_FIRST_F,
		     IB_PC_SW_PORT_VL_CONGESTION_LAST_F);
}

void mad_dump_perfcounters_rcv_con_ctrl(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_RCV_CON_CTRL_FIRST_F,
		     IB_PC_RCV_CON_CTRL_LAST_F);
}


void mad_dump_perfcounters_sl_rcv_fecn(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_SL_RCV_FECN_FIRST_F,
		     IB_PC_SL_RCV_FECN_LAST_F);
}

void mad_dump_perfcounters_sl_rcv_becn(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_SL_RCV_BECN_FIRST_F,
		     IB_PC_SL_RCV_BECN_LAST_F);
}

void mad_dump_perfcounters_xmit_con_ctrl(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_XMIT_CON_CTRL_FIRST_F,
		     IB_PC_XMIT_CON_CTRL_LAST_F);
}

void mad_dump_perfcounters_vl_xmit_time_cong(char *buf, int bufsz, void *val, int valsz)
{
	int cnt;

	cnt = _dump_fields(buf, bufsz, val, IB_PC_EXT_PORT_SELECT_F,
			   IB_PC_EXT_XMT_BYTES_F);
	if (cnt < 0)
		return;

	_dump_fields(buf + cnt, bufsz - cnt, val, IB_PC_VL_XMIT_TIME_CONG_FIRST_F,
		     IB_PC_VL_XMIT_TIME_CONG_LAST_F);
}

void mad_dump_mlnx_ext_port_info(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_MLNX_EXT_PORT_STATE_CHG_ENABLE_F,
		     IB_MLNX_EXT_PORT_LAST_F);
}

void mad_dump_cc_congestioninfo(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_INFO_FIRST_F,
		     IB_CC_CONGESTION_INFO_LAST_F);
}

void mad_dump_cc_congestionkeyinfo(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_KEY_INFO_FIRST_F,
		     IB_CC_CONGESTION_KEY_INFO_LAST_F);
}

void mad_dump_cc_congestionlog(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_LOG_FIRST_F,
		     IB_CC_CONGESTION_LOG_LAST_F);
}

void mad_dump_cc_congestionlogswitch(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_LOG_SWITCH_FIRST_F,
		     IB_CC_CONGESTION_LOG_SWITCH_LAST_F);
}

void mad_dump_cc_congestionlogentryswitch(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_LOG_ENTRY_SWITCH_FIRST_F,
		     IB_CC_CONGESTION_LOG_ENTRY_SWITCH_LAST_F);
}

void mad_dump_cc_congestionlogca(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_LOG_CA_FIRST_F,
		     IB_CC_CONGESTION_LOG_CA_LAST_F);
}

void mad_dump_cc_congestionlogentryca(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_LOG_ENTRY_CA_FIRST_F,
		     IB_CC_CONGESTION_LOG_ENTRY_CA_LAST_F);
}

void mad_dump_cc_switchcongestionsetting(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_SWITCH_CONGESTION_SETTING_FIRST_F,
		     IB_CC_SWITCH_CONGESTION_SETTING_LAST_F);
}

void mad_dump_cc_switchportcongestionsettingelement(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_SWITCH_PORT_CONGESTION_SETTING_ELEMENT_FIRST_F,
		     IB_CC_SWITCH_PORT_CONGESTION_SETTING_ELEMENT_LAST_F);
}

void mad_dump_cc_cacongestionsetting(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CA_CONGESTION_SETTING_FIRST_F,
		     IB_CC_CA_CONGESTION_SETTING_LAST_F);
}

void mad_dump_cc_cacongestionentry(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CA_CONGESTION_ENTRY_FIRST_F,
		     IB_CC_CA_CONGESTION_ENTRY_LAST_F);
}

void mad_dump_cc_congestioncontroltable(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_CONTROL_TABLE_FIRST_F,
		     IB_CC_CONGESTION_CONTROL_TABLE_LAST_F);
}

void mad_dump_cc_congestioncontroltableentry(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_CONGESTION_CONTROL_TABLE_ENTRY_FIRST_F,
		     IB_CC_CONGESTION_CONTROL_TABLE_ENTRY_LAST_F);
}

void mad_dump_cc_timestamp(char *buf, int bufsz, void *val, int valsz)
{
	_dump_fields(buf, bufsz, val, IB_CC_TIMESTAMP_FIRST_F,
		     IB_CC_TIMESTAMP_LAST_F);
}

void mad_dump_classportinfo(char *buf, int bufsz, void *val, int valsz)
{
	/* no FIRST_F and LAST_F for CPI field enums, must do a hack */
	_dump_fields(buf, bufsz, val, IB_CPI_BASEVER_F, IB_CPI_TRAP_QKEY_F + 1);
}

void mad_dump_portinfo_ext(char *buf, int bufsz, void *val, int valsz)
{
	int cnt, n;

	cnt = _dump_fields(buf, bufsz, val, IB_PORT_EXT_FIRST_F,
			   IB_PORT_EXT_LAST_F);
	if (cnt < 0)
		return;

	n = _dump_fields(buf + cnt, bufsz - cnt, val,
			 IB_PORT_EXT_HDR_FEC_MODE_SUPPORTED_F,
			 IB_PORT_EXT_HDR_FEC_MODE_LAST_F);

	if (n < 0)
		return;

	cnt += n;

	_dump_fields(buf + cnt, bufsz - cnt, val,
		     IB_PORT_EXT_NDR_FEC_MODE_SUPPORTED_F,
		     IB_PORT_EXT_NDR_FEC_MODE_LAST_F);
}

void xdump(FILE * file, const char *msg, void *p, int size)
{
#define HEX(x)  ((x) < 10 ? '0' + (x) : 'a' + ((x) -10))
	uint8_t *cp = p;
	int i;

	if (msg)
		fputs(msg, file);

	for (i = 0; i < size;) {
		fputc(HEX(*cp >> 4), file);
		fputc(HEX(*cp & 0xf), file);
		if (++i >= size)
			break;
		fputc(HEX(cp[1] >> 4), file);
		fputc(HEX(cp[1] & 0xf), file);
		if ((++i) % 16)
			fputc(' ', file);
		else
			fputc('\n', file);
		cp += 2;
	}
	if (i % 16)
		fputc('\n', file);
}
