/*
 * Copyright (c) 1985,1991,1997-1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"@(#)dlcommon.c	1.5	98/02/12 SMI"

/* LINTLIBRARY */

/*
 * Common (shared) DLPI test routines.
 * Mostly pretty boring boilerplate sorta stuff.
 * These can be split into individual library routines later
 * but it's just convenient to keep them in a single file
 * while they're being developed.
 *
 * Not supported:
 *   Connection Oriented stuff
 *   QOS stuff
 */

#include	<sys/types.h>
#include	<sys/stream.h>
#include	<sys/stropts.h>
#include	<sys/dlpi.h>
#include	<signal.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<string.h>
#include	<stropts.h>
#include	<stdarg.h>
#include	"dltest.h"
#include	<dlcommon.h>

#define		CASERET(s)	case s:  return ("s")

static void err(const char *fmt, ...);
static void syserr(char *s);

void
dlinforeq(int fd)
{
	dl_info_req_t	info_req;
	struct	strbuf	ctl;
	int	flags;

	info_req.dl_primitive = DL_INFO_REQ;

	ctl.maxlen = 0;
	ctl.len = sizeof (info_req);
	ctl.buf = (char *)&info_req;

	flags = RS_HIPRI;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlinforeq:  putmsg");
}

void
dlinfoack(int fd, char *bufp)
{
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	ctl.maxlen = MAXDLBUF;
	ctl.len = 0;
	ctl.buf = bufp;

	strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlinfoack");

	/* LINTED pointer cast */
	dlp = (union DL_primitives *)ctl.buf;

	expecting(DL_INFO_ACK, dlp);

	if (ctl.len < sizeof (dl_info_ack_t))
		err("dlinfoack:  response ctl.len too short:  %d", ctl.len);

	if (flags != RS_HIPRI)
		err("dlinfoack:  DL_INFO_ACK was not M_PCPROTO");

	if (ctl.len < sizeof (dl_info_ack_t))
		err("dlinfoack:  short response ctl.len:  %d", ctl.len);
}

void
dlattachreq(int fd, ulong_t ppa)
{
	dl_attach_req_t	attach_req;
	struct	strbuf	ctl;
	int	flags;

	attach_req.dl_primitive = DL_ATTACH_REQ;
	attach_req.dl_ppa = ppa;

	ctl.maxlen = 0;
	ctl.len = sizeof (attach_req);
	ctl.buf = (char *)&attach_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlattachreq:  putmsg");
}

void
dlenabmultireq(int fd, char *addr, int length)
{
	long	buf[MAXDLBUF];
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->enabmulti_req.dl_primitive = DL_ENABMULTI_REQ;
	dlp->enabmulti_req.dl_addr_length = length;
	dlp->enabmulti_req.dl_addr_offset = sizeof (dl_enabmulti_req_t);

	(void) memcpy(OFFADDR(buf, sizeof (dl_enabmulti_req_t)), addr, length);

	ctl.maxlen = 0;
	ctl.len = sizeof (dl_enabmulti_req_t) + length;
	ctl.buf = (char *)buf;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlenabmultireq:  putmsg");
}

void
dldisabmultireq(int fd, char *addr, int length)
{
	long	buf[MAXDLBUF];
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->disabmulti_req.dl_primitive = DL_ENABMULTI_REQ;
	dlp->disabmulti_req.dl_addr_length = length;
	dlp->disabmulti_req.dl_addr_offset = sizeof (dl_disabmulti_req_t);

	(void) memcpy(OFFADDR(buf, sizeof (dl_disabmulti_req_t)),
	    addr, length);

	ctl.maxlen = 0;
	ctl.len = sizeof (dl_disabmulti_req_t) + length;
	ctl.buf = (char *)buf;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dldisabmultireq:  putmsg");
}

void
dlpromisconreq(int fd, ulong_t level)
{
	dl_promiscon_req_t	promiscon_req;
	struct	strbuf	ctl;
	int	flags;

	promiscon_req.dl_primitive = DL_PROMISCON_REQ;
	promiscon_req.dl_level = level;

	ctl.maxlen = 0;
	ctl.len = sizeof (promiscon_req);
	ctl.buf = (char *)&promiscon_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlpromiscon:  putmsg");

}

void
dlpromiscoff(int fd, ulong_t level)
{
	dl_promiscoff_req_t	promiscoff_req;
	struct	strbuf	ctl;
	int	flags;

	promiscoff_req.dl_primitive = DL_PROMISCOFF_REQ;
	promiscoff_req.dl_level = level;

	ctl.maxlen = 0;
	ctl.len = sizeof (promiscoff_req);
	ctl.buf = (char *)&promiscoff_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlpromiscoff:  putmsg");
}

void
dlphysaddrreq(int fd, ulong_t addrtype)
{
	dl_phys_addr_req_t	phys_addr_req;
	struct	strbuf	ctl;
	int	flags;

	phys_addr_req.dl_primitive = DL_PHYS_ADDR_REQ;
	phys_addr_req.dl_addr_type = addrtype;

	ctl.maxlen = 0;
	ctl.len = sizeof (phys_addr_req);
	ctl.buf = (char *)&phys_addr_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlphysaddrreq:  putmsg");
}

void
dlsetphysaddrreq(int fd, char *addr, int length)
{
	long	buf[MAXDLBUF];
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->set_physaddr_req.dl_primitive = DL_ENABMULTI_REQ;
	dlp->set_physaddr_req.dl_addr_length = length;
	dlp->set_physaddr_req.dl_addr_offset = sizeof (dl_set_phys_addr_req_t);

	(void) memcpy(OFFADDR(buf, sizeof (dl_set_phys_addr_req_t)),
	    addr, length);

	ctl.maxlen = 0;
	ctl.len = sizeof (dl_set_phys_addr_req_t) + length;
	ctl.buf = (char *)buf;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlsetphysaddrreq:  putmsg");
}

void
dldetachreq(int fd)
{
	dl_detach_req_t	detach_req;
	struct	strbuf	ctl;
	int	flags;

	detach_req.dl_primitive = DL_DETACH_REQ;

	ctl.maxlen = 0;
	ctl.len = sizeof (detach_req);
	ctl.buf = (char *)&detach_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dldetachreq:  putmsg");
}

void
dlbindreq(int fd, ulong_t sap, ulong_t max_conind, ulong_t service_mode,
    ulong_t conn_mgmt, ulong_t xidtest)
{
	dl_bind_req_t	bind_req;
	struct	strbuf	ctl;
	int	flags;

	bind_req.dl_primitive = DL_BIND_REQ;
	bind_req.dl_sap = sap;
	bind_req.dl_max_conind = max_conind;
	bind_req.dl_service_mode = service_mode;
	bind_req.dl_conn_mgmt = conn_mgmt;
	bind_req.dl_xidtest_flg = xidtest;

	ctl.maxlen = 0;
	ctl.len = sizeof (bind_req);
	ctl.buf = (char *)&bind_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlbindreq:  putmsg");
}

void
dlunitdatareq(int fd, uchar_t *addrp, int addrlen, ulong_t minpri,
    ulong_t maxpri, uchar_t *datap, int datalen)
{
	long	buf[MAXDLBUF];
	union	DL_primitives	*dlp;
	struct	strbuf	data, ctl;

	dlp = (union DL_primitives *)buf;

	dlp->unitdata_req.dl_primitive = DL_UNITDATA_REQ;
	dlp->unitdata_req.dl_dest_addr_length = addrlen;
	dlp->unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
	dlp->unitdata_req.dl_priority.dl_min = minpri;
	dlp->unitdata_req.dl_priority.dl_max = maxpri;

	(void) memcpy(OFFADDR(dlp, sizeof (dl_unitdata_req_t)), addrp, addrlen);

	ctl.maxlen = 0;
	ctl.len = sizeof (dl_unitdata_req_t) + addrlen;
	ctl.buf = (char *)buf;

	data.maxlen = 0;
	data.len = datalen;
	data.buf = (char *)datap;

	if (putmsg(fd, &ctl, &data, 0) < 0)
		syserr("dlunitdatareq:  putmsg");
}

void
dlunbindreq(int fd)
{
	dl_unbind_req_t	unbind_req;
	struct	strbuf	ctl;
	int	flags;

	unbind_req.dl_primitive = DL_UNBIND_REQ;

	ctl.maxlen = 0;
	ctl.len = sizeof (unbind_req);
	ctl.buf = (char *)&unbind_req;

	flags = 0;

	if (putmsg(fd, &ctl, (struct strbuf *)NULL, flags) < 0)
		syserr("dlunbindreq:  putmsg");
}

void
dlokack(int fd, char *bufp)
{
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	ctl.maxlen = MAXDLBUF;
	ctl.len = 0;
	ctl.buf = bufp;

	strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlokack");

	/* LINTED pointer cast */
	dlp = (union DL_primitives *)ctl.buf;

	expecting(DL_OK_ACK, dlp);

	if (ctl.len < sizeof (dl_ok_ack_t))
		err("dlokack:  response ctl.len too short:  %d", ctl.len);

	if (flags != RS_HIPRI)
		err("dlokack:  DL_OK_ACK was not M_PCPROTO");

	if (ctl.len < sizeof (dl_ok_ack_t))
		err("dlokack:  short response ctl.len:  %d", ctl.len);
}

void
dlerrorack(int fd, char *bufp)
{
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	ctl.maxlen = MAXDLBUF;
	ctl.len = 0;
	ctl.buf = bufp;

	strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlerrorack");

	/* LINTED pointer cast */
	dlp = (union DL_primitives *)ctl.buf;

	expecting(DL_ERROR_ACK, dlp);

	if (ctl.len < sizeof (dl_error_ack_t))
		err("dlerrorack:  response ctl.len too short:  %d", ctl.len);

	if (flags != RS_HIPRI)
		err("dlerrorack:  DL_OK_ACK was not M_PCPROTO");

	if (ctl.len < sizeof (dl_error_ack_t))
		err("dlerrorack:  short response ctl.len:  %d", ctl.len);
}

void
dlbindack(int fd, char *bufp)
{
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	ctl.maxlen = MAXDLBUF;
	ctl.len = 0;
	ctl.buf = bufp;

	strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlbindack");

	/* LINTED pointer cast */
	dlp = (union DL_primitives *)ctl.buf;

	expecting(DL_BIND_ACK, dlp);

	if (flags != RS_HIPRI)
		err("dlbindack:  DL_OK_ACK was not M_PCPROTO");

	if (ctl.len < sizeof (dl_bind_ack_t))
		err("dlbindack:  short response ctl.len:  %d", ctl.len);
}

void
dlphysaddrack(int fd, char *bufp)
{
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	ctl.maxlen = MAXDLBUF;
	ctl.len = 0;
	ctl.buf = bufp;

	strgetmsg(fd, &ctl, (struct strbuf *)NULL, &flags, "dlphysaddrack");

	/* LINTED pointer cast */
	dlp = (union DL_primitives *)ctl.buf;

	expecting(DL_PHYS_ADDR_ACK, dlp);

	if (flags != RS_HIPRI)
		err("dlbindack:  DL_OK_ACK was not M_PCPROTO");

	if (ctl.len < sizeof (dl_phys_addr_ack_t))
		err("dlphysaddrack:  short response ctl.len:  %d", ctl.len);
}

/*ARGSUSED*/
static void
sigalrm(int s)
{
	(void) err("sigalrm:  TIMEOUT");
}

void
strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap, int *flagsp,
    char *caller)
{
	int	rc;
	static	char	errmsg[80];

	/*
	 * Start timer.
	 */
	(void) signal(SIGALRM, sigalrm);
	(void) alarm(MAXWAIT);

	/*
	 * Set flags argument and issue getmsg().
	 */
	*flagsp = 0;
	if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
		(void) sprintf(errmsg, "%s:  getmsg", caller);
		syserr(errmsg);
	}

	/*
	 * Stop timer.
	 */
	(void) alarm(0);

	/*
	 * Check for MOREDATA and/or MORECTL.
	 */
	if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA))
		err("%s:  MORECTL|MOREDATA", caller);
	if (rc & MORECTL)
		err("%s:  MORECTL", caller);
	if (rc & MOREDATA)
		err("%s:  MOREDATA", caller);

	/*
	 * Check for at least sizeof (long) control data portion.
	 */
	if (ctlp->len < sizeof (long))
		err("getmsg:  control portion length < sizeof (long):  %d",
		    ctlp->len);
}

void
expecting(int prim, union DL_primitives *dlp)
{
	if (dlp->dl_primitive != (ulong_t)prim) {
		printdlprim(dlp);
		err("expected %s got %s", dlprim(prim),
		    dlprim(dlp->dl_primitive));
		exit(1);
	}
}

/*
 * Print any DLPI msg in human readable format.
 */
void
printdlprim(union DL_primitives *dlp)
{
	switch (dlp->dl_primitive) {
	case DL_INFO_REQ:
		printdlinforeq(dlp);
		break;

	case DL_INFO_ACK:
		printdlinfoack(dlp);
		break;

	case DL_ATTACH_REQ:
		printdlattachreq(dlp);
		break;

	case DL_OK_ACK:
		printdlokack(dlp);
		break;

	case DL_ERROR_ACK:
		printdlerrorack(dlp);
		break;

	case DL_DETACH_REQ:
		printdldetachreq(dlp);
		break;

	case DL_BIND_REQ:
		printdlbindreq(dlp);
		break;

	case DL_BIND_ACK:
		printdlbindack(dlp);
		break;

	case DL_UNBIND_REQ:
		printdlunbindreq(dlp);
		break;

	case DL_SUBS_BIND_REQ:
		printdlsubsbindreq(dlp);
		break;

	case DL_SUBS_BIND_ACK:
		printdlsubsbindack(dlp);
		break;

	case DL_SUBS_UNBIND_REQ:
		printdlsubsunbindreq(dlp);
		break;

	case DL_ENABMULTI_REQ:
		printdlenabmultireq(dlp);
		break;

	case DL_DISABMULTI_REQ:
		printdldisabmultireq(dlp);
		break;

	case DL_PROMISCON_REQ:
		printdlpromisconreq(dlp);
		break;

	case DL_PROMISCOFF_REQ:
		printdlpromiscoffreq(dlp);
		break;

	case DL_UNITDATA_REQ:
		printdlunitdatareq(dlp);
		break;

	case DL_UNITDATA_IND:
		printdlunitdataind(dlp);
		break;

	case DL_UDERROR_IND:
		printdluderrorind(dlp);
		break;

	case DL_UDQOS_REQ:
		printdludqosreq(dlp);
		break;

	case DL_PHYS_ADDR_REQ:
		printdlphysaddrreq(dlp);
		break;

	case DL_PHYS_ADDR_ACK:
		printdlphysaddrack(dlp);
		break;

	case DL_SET_PHYS_ADDR_REQ:
		printdlsetphysaddrreq(dlp);
		break;

	default:
		err("printdlprim:  unknown primitive type 0x%x",
			dlp->dl_primitive);
		break;
	}
}

/* ARGSUSED */
void
printdlinforeq(union DL_primitives *dlp)
{
	(void) printf("DL_INFO_REQ\n");
}

void
printdlinfoack(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];
	uchar_t	brdcst[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->info_ack.dl_addr_offset),
	    dlp->info_ack.dl_addr_length, addr);
	addrtostring(OFFADDR(dlp, dlp->info_ack.dl_brdcst_addr_offset),
	    dlp->info_ack.dl_brdcst_addr_length, brdcst);

	(void) printf("DL_INFO_ACK:  max_sdu %d min_sdu %d\n",
	    dlp->info_ack.dl_max_sdu,
	    dlp->info_ack.dl_min_sdu);
	(void) printf("addr_length %d mac_type %s current_state %s\n",
	    dlp->info_ack.dl_addr_length,
	    dlmactype(dlp->info_ack.dl_mac_type),
	    dlstate(dlp->info_ack.dl_current_state));
	(void) printf("sap_length %d service_mode %s qos_length %d\n",
	    dlp->info_ack.dl_sap_length,
	    dlservicemode(dlp->info_ack.dl_service_mode),
	    dlp->info_ack.dl_qos_length);
	(void) printf("qos_offset %d qos_range_length %d qos_range_offset %d\n",
	    dlp->info_ack.dl_qos_offset,
	    dlp->info_ack.dl_qos_range_length,
	    dlp->info_ack.dl_qos_range_offset);
	(void) printf("provider_style %s addr_offset %d version %d\n",
	    dlstyle(dlp->info_ack.dl_provider_style),
	    dlp->info_ack.dl_addr_offset,
	    dlp->info_ack.dl_version);
	(void) printf("brdcst_addr_length %d brdcst_addr_offset %d\n",
	    dlp->info_ack.dl_brdcst_addr_length,
	    dlp->info_ack.dl_brdcst_addr_offset);
	(void) printf("addr %s\n", addr);
	(void) printf("brdcst_addr %s\n", brdcst);
}

void
printdlattachreq(union DL_primitives *dlp)
{
	(void) printf("DL_ATTACH_REQ:  ppa %d\n",
	    dlp->attach_req.dl_ppa);
}

void
printdlokack(union DL_primitives *dlp)
{
	(void) printf("DL_OK_ACK:  correct_primitive %s\n",
	    dlprim(dlp->ok_ack.dl_correct_primitive));
}

void
printdlerrorack(union DL_primitives *dlp)
{
	(void) printf(
	    "DL_ERROR_ACK:  error_primitive %s errno %s unix_errno %d\n",
	    dlprim(dlp->error_ack.dl_error_primitive),
	    dlerrno(dlp->error_ack.dl_errno),
	    dlp->error_ack.dl_unix_errno);
}

void
printdlenabmultireq(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->enabmulti_req.dl_addr_offset),
	    dlp->enabmulti_req.dl_addr_length, addr);

	(void) printf("DL_ENABMULTI_REQ:  addr_length %d addr_offset %d\n",
	    dlp->enabmulti_req.dl_addr_length,
	    dlp->enabmulti_req.dl_addr_offset);
	(void) printf("addr %s\n", addr);
}

void
printdldisabmultireq(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->disabmulti_req.dl_addr_offset),
	    dlp->disabmulti_req.dl_addr_length, addr);

	(void) printf("DL_DISABMULTI_REQ:  addr_length %d addr_offset %d\n",
	    dlp->disabmulti_req.dl_addr_length,
	    dlp->disabmulti_req.dl_addr_offset);
	(void) printf("addr %s\n", addr);
}

void
printdlpromisconreq(union DL_primitives *dlp)
{
	(void) printf("DL_PROMISCON_REQ:  level %s\n",
	    dlpromisclevel(dlp->promiscon_req.dl_level));
}

void
printdlpromiscoffreq(union DL_primitives *dlp)
{
	(void) printf("DL_PROMISCOFF_REQ:  level %s\n",
	    dlpromisclevel(dlp->promiscoff_req.dl_level));
}

void
printdlphysaddrreq(union DL_primitives *dlp)
{
	(void) printf("DL_PHYS_ADDR_REQ:  addr_type 0x%x\n",
	    dlp->physaddr_req.dl_addr_type);
}

void
printdlphysaddrack(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->physaddr_ack.dl_addr_offset),
	    dlp->physaddr_ack.dl_addr_length, addr);

	(void) printf("DL_PHYS_ADDR_ACK:  addr_length %d addr_offset %d\n",
	    dlp->physaddr_ack.dl_addr_length,
	    dlp->physaddr_ack.dl_addr_offset);
	(void) printf("addr %s\n", addr);
}

void
printdlsetphysaddrreq(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->set_physaddr_req.dl_addr_offset),
	    dlp->set_physaddr_req.dl_addr_length, addr);

	(void) printf("DL_SET_PHYS_ADDR_REQ:  addr_length %d addr_offset %d\n",
	    dlp->set_physaddr_req.dl_addr_length,
	    dlp->set_physaddr_req.dl_addr_offset);
	(void) printf("addr %s\n", addr);
}

/* ARGSUSED */
void
printdldetachreq(union DL_primitives *dlp)
{
	(void) printf("DL_DETACH_REQ\n");
}

void
printdlbindreq(union DL_primitives *dlp)
{
	(void) printf("DL_BIND_REQ:  sap %d max_conind %d\n",
	    dlp->bind_req.dl_sap,
	    dlp->bind_req.dl_max_conind);
	(void) printf("service_mode %s conn_mgmt %d xidtest_flg 0x%x\n",
	    dlservicemode(dlp->bind_req.dl_service_mode),
	    dlp->bind_req.dl_conn_mgmt,
	    dlp->bind_req.dl_xidtest_flg);
}

void
printdlbindack(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->bind_ack.dl_addr_offset),
	    dlp->bind_ack.dl_addr_length, addr);

	(void) printf("DL_BIND_ACK:  sap %d addr_length %d addr_offset %d\n",
	    dlp->bind_ack.dl_sap,
	    dlp->bind_ack.dl_addr_length,
	    dlp->bind_ack.dl_addr_offset);
	(void) printf("max_conind %d xidtest_flg 0x%x\n",
	    dlp->bind_ack.dl_max_conind,
	    dlp->bind_ack.dl_xidtest_flg);
	(void) printf("addr %s\n", addr);
}

/* ARGSUSED */
void
printdlunbindreq(union DL_primitives *dlp)
{
	(void) printf("DL_UNBIND_REQ\n");
}

void
printdlsubsbindreq(union DL_primitives *dlp)
{
	uchar_t	sap[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->subs_bind_req.dl_subs_sap_offset),
	    dlp->subs_bind_req.dl_subs_sap_length, sap);

	(void) printf("DL_SUBS_BIND_REQ:  subs_sap_offset %d sub_sap_len %d\n",
	    dlp->subs_bind_req.dl_subs_sap_offset,
	    dlp->subs_bind_req.dl_subs_sap_length);
	(void) printf("sap %s\n", sap);
}

void
printdlsubsbindack(union DL_primitives *dlp)
{
	uchar_t	sap[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->subs_bind_ack.dl_subs_sap_offset),
	    dlp->subs_bind_ack.dl_subs_sap_length, sap);

	(void) printf(
	    "DL_SUBS_BIND_ACK:  subs_sap_offset %d sub_sap_length %d\n",
	    dlp->subs_bind_ack.dl_subs_sap_offset,
	    dlp->subs_bind_ack.dl_subs_sap_length);
	(void) printf("sap %s\n", sap);
}

void
printdlsubsunbindreq(union DL_primitives *dlp)
{
	uchar_t	sap[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->subs_unbind_req.dl_subs_sap_offset),
	    dlp->subs_unbind_req.dl_subs_sap_length, sap);

	(void) printf(
	    "DL_SUBS_UNBIND_REQ:  subs_sap_offset %d sub_sap_length %d\n",
	    dlp->subs_unbind_req.dl_subs_sap_offset,
	    dlp->subs_unbind_req.dl_subs_sap_length);
	(void) printf("sap %s\n", sap);
}

void
printdlunitdatareq(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->unitdata_req.dl_dest_addr_offset),
	    dlp->unitdata_req.dl_dest_addr_length, addr);

	(void) printf(
	    "DL_UNITDATA_REQ:  dest_addr_length %d dest_addr_offset %d\n",
	    dlp->unitdata_req.dl_dest_addr_length,
	    dlp->unitdata_req.dl_dest_addr_offset);
	(void) printf("dl_priority.min %d dl_priority.max %d\n",
	    dlp->unitdata_req.dl_priority.dl_min,
	    dlp->unitdata_req.dl_priority.dl_max);
	(void) printf("addr %s\n", addr);
}

void
printdlunitdataind(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];
	uchar_t	src[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->unitdata_ind.dl_dest_addr_offset),
	    dlp->unitdata_ind.dl_dest_addr_length, dest);
	addrtostring(OFFADDR(dlp, dlp->unitdata_ind.dl_src_addr_offset),
	    dlp->unitdata_ind.dl_src_addr_length, src);

	(void) printf(
	    "DL_UNITDATA_IND:  dest_addr_length %d dest_addr_offset %d\n",
	    dlp->unitdata_ind.dl_dest_addr_length,
	    dlp->unitdata_ind.dl_dest_addr_offset);
	(void) printf("src_addr_length %d src_addr_offset %d\n",
	    dlp->unitdata_ind.dl_src_addr_length,
	    dlp->unitdata_ind.dl_src_addr_offset);
	(void) printf("group_address 0x%x\n",
	    dlp->unitdata_ind.dl_group_address);
	(void) printf("dest %s\n", dest);
	(void) printf("src %s\n", src);
}

void
printdluderrorind(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->uderror_ind.dl_dest_addr_offset),
	    dlp->uderror_ind.dl_dest_addr_length, addr);

	(void) printf(
	    "DL_UDERROR_IND:  dest_addr_length %d dest_addr_offset %d\n",
	    dlp->uderror_ind.dl_dest_addr_length,
	    dlp->uderror_ind.dl_dest_addr_offset);
	(void) printf("unix_errno %d errno %s\n",
	    dlp->uderror_ind.dl_unix_errno,
	    dlerrno(dlp->uderror_ind.dl_errno));
	(void) printf("addr %s\n", addr);
}

void
printdltestreq(union DL_primitives *dlp)
{
	uchar_t	addr[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->test_req.dl_dest_addr_offset),
	    dlp->test_req.dl_dest_addr_length, addr);

	(void) printf(
	    "DL_TEST_REQ:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->test_req.dl_flag,
	    dlp->test_req.dl_dest_addr_length,
	    dlp->test_req.dl_dest_addr_offset);
	(void) printf("dest_addr %s\n", addr);
}

void
printdltestind(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];
	uchar_t	src[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->test_ind.dl_dest_addr_offset),
	    dlp->test_ind.dl_dest_addr_length, dest);
	addrtostring(OFFADDR(dlp, dlp->test_ind.dl_src_addr_offset),
	    dlp->test_ind.dl_src_addr_length, src);

	(void) printf(
	    "DL_TEST_IND:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->test_ind.dl_flag,
	    dlp->test_ind.dl_dest_addr_length,
	    dlp->test_ind.dl_dest_addr_offset);
	(void) printf("src_addr_length %d src_addr_offset %d\n",
	    dlp->test_ind.dl_src_addr_length,
	    dlp->test_ind.dl_src_addr_offset);
	(void) printf("dest_addr %s\n", dest);
	(void) printf("src_addr %s\n", src);
}

void
printdltestres(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->test_res.dl_dest_addr_offset),
	    dlp->test_res.dl_dest_addr_length, dest);

	(void) printf(
	    "DL_TEST_RES:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->test_res.dl_flag,
	    dlp->test_res.dl_dest_addr_length,
	    dlp->test_res.dl_dest_addr_offset);
	(void) printf("dest_addr %s\n", dest);
}

void
printdltestcon(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];
	uchar_t	src[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->test_con.dl_dest_addr_offset),
	    dlp->test_con.dl_dest_addr_length, dest);
	addrtostring(OFFADDR(dlp, dlp->test_con.dl_src_addr_offset),
	    dlp->test_con.dl_src_addr_length, src);

	(void) printf(
	    "DL_TEST_CON:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->test_con.dl_flag,
	    dlp->test_con.dl_dest_addr_length,
	    dlp->test_con.dl_dest_addr_offset);
	(void) printf("src_addr_length %d src_addr_offset %d\n",
	    dlp->test_con.dl_src_addr_length,
	    dlp->test_con.dl_src_addr_offset);
	(void) printf("dest_addr %s\n", dest);
	(void) printf("src_addr %s\n", src);
}

void
printdlxidreq(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->xid_req.dl_dest_addr_offset),
	    dlp->xid_req.dl_dest_addr_length, dest);

	(void) printf(
	    "DL_XID_REQ:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->xid_req.dl_flag,
	    dlp->xid_req.dl_dest_addr_length,
	    dlp->xid_req.dl_dest_addr_offset);
	(void) printf("dest_addr %s\n", dest);
}

void
printdlxidind(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];
	uchar_t	src[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->xid_ind.dl_dest_addr_offset),
	    dlp->xid_ind.dl_dest_addr_length, dest);
	addrtostring(OFFADDR(dlp, dlp->xid_ind.dl_src_addr_offset),
	    dlp->xid_ind.dl_src_addr_length, src);

	(void) printf(
	    "DL_XID_IND:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->xid_ind.dl_flag,
	    dlp->xid_ind.dl_dest_addr_length,
	    dlp->xid_ind.dl_dest_addr_offset);
	(void) printf("src_addr_length %d src_addr_offset %d\n",
	    dlp->xid_ind.dl_src_addr_length,
	    dlp->xid_ind.dl_src_addr_offset);
	(void) printf("dest_addr %s\n", dest);
	(void) printf("src_addr %s\n", src);
}

void
printdlxidres(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->xid_res.dl_dest_addr_offset),
	    dlp->xid_res.dl_dest_addr_length, dest);

	(void) printf(
	    "DL_XID_RES:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->xid_res.dl_flag,
	    dlp->xid_res.dl_dest_addr_length,
	    dlp->xid_res.dl_dest_addr_offset);
	(void) printf("dest_addr %s\n", dest);
}

void
printdlxidcon(union DL_primitives *dlp)
{
	uchar_t	dest[MAXDLADDR];
	uchar_t	src[MAXDLADDR];

	addrtostring(OFFADDR(dlp, dlp->xid_con.dl_dest_addr_offset),
	    dlp->xid_con.dl_dest_addr_length, dest);
	addrtostring(OFFADDR(dlp, dlp->xid_con.dl_src_addr_offset),
	    dlp->xid_con.dl_src_addr_length, src);

	(void) printf(
	    "DL_XID_CON:  flag 0x%x dest_addr_length %d dest_addr_offset %d\n",
	    dlp->xid_con.dl_flag,
	    dlp->xid_con.dl_dest_addr_length,
	    dlp->xid_con.dl_dest_addr_offset);
	(void) printf("src_addr_length %d src_addr_offset %d\n",
	    dlp->xid_con.dl_src_addr_length,
	    dlp->xid_con.dl_src_addr_offset);
	(void) printf("dest_addr %s\n", dest);
	(void) printf("src_addr %s\n", src);
}

void
printdludqosreq(union DL_primitives *dlp)
{
	(void) printf("DL_UDQOS_REQ:  qos_length %d qos_offset %d\n",
	    dlp->udqos_req.dl_qos_length,
	    dlp->udqos_req.dl_qos_offset);
}

/*
 * Return string.
 */
void
addrtostring(uchar_t *addr, ulong_t length, uchar_t *s)
{
	int	i;

	for (i = 0; i < length; i++) {
		(void) sprintf((char *)s, "%x:", addr[i] & 0xff);
		s = s + strlen((char *)s);
	}
	if (length)
		*(--s) = '\0';
}

/*
 * Return length
 */
int
stringtoaddr(char *sp, char *addr)
{
	int	n = 0;
	char	*p;
	int	val;

	p = sp;
	while (p = strtok(p, ":")) {
		if (sscanf(p, "%x", &val) != 1)
			err("stringtoaddr:  invalid input string:  %s", sp);
		if (val > 0xff)
			err("stringtoaddr:  invalid input string:  %s", sp);
		*addr++ = val;
		n++;
		p = NULL;
	}
	return (n);
}


#ifdef notdef
static char
hexnibble(char c)
{
	static	char	hextab[] = {
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'a', 'b', 'c', 'd', 'e', 'f'
	};

	return (hextab[c & 0x0f]);
}
#endif

char *
dlprim(ulong_t prim)
{
	static	char	primbuf[80];

	switch ((int)prim) {
	CASERET(DL_INFO_REQ);
	CASERET(DL_INFO_ACK);
	CASERET(DL_ATTACH_REQ);
	CASERET(DL_DETACH_REQ);
	CASERET(DL_BIND_REQ);
	CASERET(DL_BIND_ACK);
	CASERET(DL_UNBIND_REQ);
	CASERET(DL_OK_ACK);
	CASERET(DL_ERROR_ACK);
	CASERET(DL_SUBS_BIND_REQ);
	CASERET(DL_SUBS_BIND_ACK);
	CASERET(DL_UNITDATA_REQ);
	CASERET(DL_UNITDATA_IND);
	CASERET(DL_UDERROR_IND);
	CASERET(DL_UDQOS_REQ);
	CASERET(DL_CONNECT_REQ);
	CASERET(DL_CONNECT_IND);
	CASERET(DL_CONNECT_RES);
	CASERET(DL_CONNECT_CON);
	CASERET(DL_TOKEN_REQ);
	CASERET(DL_TOKEN_ACK);
	CASERET(DL_DISCONNECT_REQ);
	CASERET(DL_DISCONNECT_IND);
	CASERET(DL_RESET_REQ);
	CASERET(DL_RESET_IND);
	CASERET(DL_RESET_RES);
	CASERET(DL_RESET_CON);

	default:
		(void) sprintf(primbuf, "unknown primitive 0x%x", prim);
		return (primbuf);
	}
}


char *
dlstate(ulong_t state)
{
	static	char	statebuf[80];

	switch (state) {
	CASERET(DL_UNATTACHED);
	CASERET(DL_ATTACH_PENDING);
	CASERET(DL_DETACH_PENDING);
	CASERET(DL_UNBOUND);
	CASERET(DL_BIND_PENDING);
	CASERET(DL_UNBIND_PENDING);
	CASERET(DL_IDLE);
	CASERET(DL_UDQOS_PENDING);
	CASERET(DL_OUTCON_PENDING);
	CASERET(DL_INCON_PENDING);
	CASERET(DL_CONN_RES_PENDING);
	CASERET(DL_DATAXFER);
	CASERET(DL_USER_RESET_PENDING);
	CASERET(DL_PROV_RESET_PENDING);
	CASERET(DL_RESET_RES_PENDING);
	CASERET(DL_DISCON8_PENDING);
	CASERET(DL_DISCON9_PENDING);
	CASERET(DL_DISCON11_PENDING);
	CASERET(DL_DISCON12_PENDING);
	CASERET(DL_DISCON13_PENDING);
	CASERET(DL_SUBS_BIND_PND);

	default:
		(void) sprintf(statebuf, "unknown state 0x%x", state);
		return (statebuf);
	}
}

char *
dlerrno(ulong_t errno)
{
	static	char	errnobuf[80];

	switch (errno) {
	CASERET(DL_ACCESS);
	CASERET(DL_BADADDR);
	CASERET(DL_BADCORR);
	CASERET(DL_BADDATA);
	CASERET(DL_BADPPA);
	CASERET(DL_BADPRIM);
	CASERET(DL_BADQOSPARAM);
	CASERET(DL_BADQOSTYPE);
	CASERET(DL_BADSAP);
	CASERET(DL_BADTOKEN);
	CASERET(DL_BOUND);
	CASERET(DL_INITFAILED);
	CASERET(DL_NOADDR);
	CASERET(DL_NOTINIT);
	CASERET(DL_OUTSTATE);
	CASERET(DL_SYSERR);
	CASERET(DL_UNSUPPORTED);
	CASERET(DL_UNDELIVERABLE);
	CASERET(DL_NOTSUPPORTED);
	CASERET(DL_TOOMANY);
	CASERET(DL_NOTENAB);
	CASERET(DL_BUSY);
	CASERET(DL_NOAUTO);
	CASERET(DL_NOXIDAUTO);
	CASERET(DL_NOTESTAUTO);
	CASERET(DL_XIDAUTO);
	CASERET(DL_TESTAUTO);
	CASERET(DL_PENDING);

	default:
		(void) sprintf(errnobuf,
		    "unknown dlpi errno 0x%x", errno);
		return (errnobuf);
	}
}

char *
dlpromisclevel(ulong_t level)
{
	static	char	levelbuf[80];

	switch (level) {
	CASERET(DL_PROMISC_PHYS);
	CASERET(DL_PROMISC_SAP);
	CASERET(DL_PROMISC_MULTI);

	default:
		(void) sprintf(levelbuf,
		    "unknown promisc level 0x%x", level);
		return (levelbuf);
	}
}

char *
dlservicemode(ulong_t servicemode)
{
	static	char	servicemodebuf[80];

	switch (servicemode) {
	CASERET(DL_CODLS);
	CASERET(DL_CLDLS);
	CASERET(DL_CODLS|DL_CLDLS);

	default:
		(void) sprintf(servicemodebuf,
		    "unknown provider service mode 0x%x", servicemode);
		return (servicemodebuf);
	}
}

char *
dlstyle(long style)
{
	static	char	stylebuf[80];

	switch (style) {
	CASERET(DL_STYLE1);
	CASERET(DL_STYLE2);

	default:
		(void) sprintf(stylebuf,
		    "unknown provider style 0x%x", style);
		return (stylebuf);
	}
}

char *
dlmactype(ulong_t media)
{
	static	char	mediabuf[80];

	switch (media) {
	CASERET(DL_CSMACD);
	CASERET(DL_TPB);
	CASERET(DL_TPR);
	CASERET(DL_METRO);
	CASERET(DL_ETHER);
	CASERET(DL_HDLC);
	CASERET(DL_CHAR);
	CASERET(DL_CTCA);
	CASERET(DL_FC);
	CASERET(DL_ATM);
	CASERET(DL_IPATM);
	CASERET(DL_X25);
	CASERET(DL_ISDN);
	CASERET(DL_HIPPI);
	CASERET(DL_100VG);
	CASERET(DL_100VGTPR);
	CASERET(DL_ETH_CSMA);
	CASERET(DL_100BT);
	CASERET(DL_FRAME);
	CASERET(DL_MPFRAME);
	CASERET(DL_ASYNC);
	CASERET(DL_IPX25);
	CASERET(DL_LOOP);
	CASERET(DL_OTHER);

	default:
		(void) sprintf(mediabuf,
		    "unknown media type 0x%x", media);
		return (mediabuf);
	}
}

static void
err(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	(void) vfprintf(stderr, fmt, ap);
	va_end(ap);
	(void) fprintf(stderr, "\n");
	(void) exit(1);
}

static void
syserr(char *s)
{
	(void) perror(s);
	exit(1);
}

int
strioctl(int fd, int cmd, int timout, int len, char *dp)
{
	struct	strioctl	sioc;
	int	rc;

	sioc.ic_cmd = cmd;
	sioc.ic_timout = timout;
	sioc.ic_len = len;
	sioc.ic_dp = dp;
	rc = ioctl(fd, I_STR, &sioc);

	if (rc < 0)
		return (rc);
	else
		return (sioc.ic_len);
}
