/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/param.h>		/* for NULL */
#include <sys/sbd_ioctl.h>
#include <sys/dr_util.h>
#include <sys/varargs.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>

/* sbd_etab[] and sbd_etab_len provided by sbdgenerr.pl */
extern sbd_etab_t sbd_etab[];
extern int sbd_etab_len;

sbd_error_t *
sbd_err_new(int e_code, char *fmt, va_list args)
{
	sbd_error_t	*new;

	new = GETSTRUCT(sbd_error_t, 1);
	new->e_code = e_code;

	if (fmt)
		(void) vsnprintf(new->e_rsc, sizeof (new->e_rsc), fmt, args);

	return (new);
}

void
sbd_err_log(sbd_error_t *ep, int ce)
{
	char		 buf[32];
	char		*fmt;
	char		*txt;
	int		 i;
	sbd_etab_t	*tp;

	if (!ep)
		return;

	if (ep->e_rsc[0] == '\0')
		fmt = "%s";
	else
		fmt = "%s: %s";

	for (tp = sbd_etab, i = 0; i < sbd_etab_len; i++, tp++)
		if (ep->e_code >= tp->t_base && ep->e_code <= tp->t_bnd)
			break;

	if (i < sbd_etab_len)
		txt = tp->t_text[ep->e_code - tp->t_base];
	else {
		snprintf(buf, sizeof (buf), "error %d", ep->e_code);
		txt = buf;
	}

	cmn_err(ce, fmt, txt, ep->e_rsc);
}

void
sbd_err_clear(sbd_error_t **ep)
{
	FREESTRUCT(*ep, sbd_error_t, 1);
	*ep = NULL;
}

void
sbd_err_set_c(sbd_error_t **ep, int ce, int e_code, char *fmt, ...)
{
	sbd_error_t	*tmp;
	va_list		args;

	va_start(args, fmt);

	tmp = sbd_err_new(e_code, fmt, args);

	sbd_err_log(tmp, ce);

	if (*ep == NULL)
		*ep = tmp;
	else
		sbd_err_clear(&tmp);

	va_end(args);
}

void
sbd_err_set(sbd_error_t **ep, int ce, int e_code, char *fmt, ...)
{
	sbd_error_t	*tmp;
	va_list		args;

	va_start(args, fmt);

	tmp = sbd_err_new(e_code, fmt, args);

	sbd_err_log(tmp, ce);

	*ep = tmp;

	va_end(args);
}

sbd_error_t *
drerr_new_v(int e_code, char *fmt, va_list args)
{
	return (sbd_err_new(e_code, fmt, args));
}

sbd_error_t *
drerr_new(int log, int e_code, char *fmt, ...)
{
	sbd_error_t	*ep;
	va_list		 args;

	va_start(args, fmt);
	ep = sbd_err_new(e_code, fmt, args);
	va_end(args);

	if (log)
		sbd_err_log(ep, CE_WARN);

	return (ep);
}

void
drerr_set_c(int log, sbd_error_t **ep, int e_code, char *fmt, ...)
{
	sbd_error_t	*err;
	va_list		 args;

	va_start(args, fmt);
	err = sbd_err_new(e_code, fmt, args);
	va_end(args);

	if (log)
		sbd_err_log(err, CE_WARN);

	if (*ep == NULL)
		*ep = err;
	else
		sbd_err_clear(&err);
}


/*
 * Memlist support.
 */
void
memlist_delete(struct memlist *mlist)
{
	register struct memlist	*ml;

	for (ml = mlist; ml; ml = mlist) {
		mlist = ml->next;
		FREESTRUCT(ml, struct memlist, 1);
	}
}

int
memlist_intersect(struct memlist *al, struct memlist *bl)
{
	uint64_t	astart, aend, bstart, bend;

	if ((al == NULL) || (bl == NULL))
		return (0);

	aend = al->address + al->size;
	bstart = bl->address;
	bend = bl->address + bl->size;

	while (al && bl) {
		while (al && (aend <= bstart))
			if ((al = al->next) != NULL)
				aend = al->address + al->size;
		if (al == NULL)
			return (0);

		if ((astart = al->address) <= bstart)
			return (1);

		while (bl && (bend <= astart))
			if ((bl = bl->next) != NULL)
				bend = bl->address + bl->size;
		if (bl == NULL)
			return (0);

		if ((bstart = bl->address) <= astart)
			return (1);
	}

	return (0);
}

void
memlist_coalesce(struct memlist *mlist)
{
	uint64_t	end, nend;

	if ((mlist == NULL) || (mlist->next == NULL))
		return;

	while (mlist->next) {
		end = mlist->address + mlist->size;
		if (mlist->next->address <= end) {
			struct memlist 	*nl;

			nend = mlist->next->address + mlist->next->size;
			if (nend > end)
				mlist->size += (nend - end);
			nl = mlist->next;
			mlist->next = mlist->next->next;
			if (nl) {
				FREESTRUCT(nl, struct memlist, 1);
			}
			if (mlist->next)
				mlist->next->prev = mlist;
		} else {
			mlist = mlist->next;
		}
	}
}

#ifdef DEBUG
void
memlist_dump(struct memlist *mlist)
{
	register struct memlist *ml;

	if (mlist == NULL)
		printf("memlist> EMPTY\n");
	else for (ml = mlist; ml; ml = ml->next)
		printf("memlist> 0x%lx, 0x%lx\n", ml->address, ml->size);
}
#endif