/*	$FreeBSD$	*/

/*
 * 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
 */

/*
typedef	unsigned long	ulong;
*/


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

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

	char	*dlprim();
	char	*dlstate();
	char	*dlerrno();
	char	*dlpromisclevel();
	char	*dlservicemode();
	char	*dlstyle();
	char	*dlmactype();


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");

	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, u_long 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((char*)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((char*)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, u_long 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, u_long 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, u_long 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((char*)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, u_long sap, u_long max_conind, u_long service_mode,
	u_long conn_mgmt, u_long 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, u_char *addrp, int addrlen, u_long minpri,
	u_long maxpri, u_char *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");

	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");

	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");

	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");

	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);
}

void
sigalrm(void)
{
	(void) err("sigalrm:  TIMEOUT");
}

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);
	if (alarm(MAXWAIT) < 0) {
		(void) snprintf(errmsg, sizeof(errmsg), "%s:  alarm", caller);
		syserr(errmsg);
	}

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

	/*
	 * Stop timer.
	 */
	if (alarm(0) < 0) {
		(void) snprintf(errmsg, sizeof(errmsg), "%s:  alarm", caller);
		syserr(errmsg);
	}

	/*
	 * 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);
}

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

/*
 * Print any DLPI msg in human readable format.
 */
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 */
printdlinforeq(union DL_primitives *dlp)
{
	(void) printf("DL_INFO_REQ\n");
}

printdlinfoack(union DL_primitives *dlp)
{
	u_char	addr[MAXDLADDR];
	u_char	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);
}

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

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

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

printdlenabmultireq(union DL_primitives *dlp)
{
	u_char	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);
}

printdldisabmultireq(union DL_primitives *dlp)
{
	u_char	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);
}

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

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

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

printdlphysaddrack(union DL_primitives *dlp)
{
	u_char	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);
}

printdlsetphysaddrreq(union DL_primitives *dlp)
{
	u_char	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 */
printdldetachreq(union DL_primitives *dlp)
{
	(void) printf("DL_DETACH_REQ\n");
}

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);
}

printdlbindack(union DL_primitives *dlp)
{
	u_char	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 */
printdlunbindreq(union DL_primitives *dlp)
{
	(void) printf("DL_UNBIND_REQ\n");
}

printdlsubsbindreq(union DL_primitives *dlp)
{
	u_char	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);
}

printdlsubsbindack(union DL_primitives *dlp)
{
	u_char	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);
}

printdlsubsunbindreq(union DL_primitives *dlp)
{
	u_char	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);
}

printdlunitdatareq(union DL_primitives *dlp)
{
	u_char	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);
}

printdlunitdataind(union DL_primitives *dlp)
{
	u_char	dest[MAXDLADDR];
	u_char	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);
}

printdluderrorind(union DL_primitives *dlp)
{
	u_char	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);
}

printdltestreq(union DL_primitives *dlp)
{
	u_char	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);
}

printdltestind(union DL_primitives *dlp)
{
	u_char	dest[MAXDLADDR];
	u_char	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);
}

printdltestres(union DL_primitives *dlp)
{
	u_char	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);
}

printdltestcon(union DL_primitives *dlp)
{
	u_char	dest[MAXDLADDR];
	u_char	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);
}

printdlxidreq(union DL_primitives *dlp)
{
	u_char	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);
}

printdlxidind(dlpunion DL_primitives *)
{
	u_char	dest[MAXDLADDR];
	u_char	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);
}

printdlxidres(union DL_primitives *dlp)
{
	u_char	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);
}

printdlxidcon(union DL_primitives *dlp)
{
	u_char	dest[MAXDLADDR];
	u_char	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);
}

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.
 */
addrtostring(u_char *addr, u_long length, u_char *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
 */
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);
}


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]);
}

char*
dlprim(u_long 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) snprintf(primbuf, sizeof(primbuf), "unknown primitive 0x%x", prim);
			return (primbuf);
	}
}


char*
dlstate(u_long 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) snprintf(statebuf, sizeof(statebuf), "unknown state 0x%x", state);
			return (statebuf);
	}
}

char*
dlerrno(u_long 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) snprintf(errnobuf, sizeof(errnobuf), "unknown dlpi errno 0x%x", errno);
			return (errnobuf);
	}
}

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

	switch (level) {
		CASERET(DL_PROMISC_PHYS);
		CASERET(DL_PROMISC_SAP);
		CASERET(DL_PROMISC_MULTI);
		default:
			(void) snprintf(levelbuf, sizeof(levelbuf), "unknown promisc level 0x%x", level);
			return (levelbuf);
	}
}

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

	switch (servicemode) {
		CASERET(DL_CODLS);
		CASERET(DL_CLDLS);
		CASERET(DL_CODLS|DL_CLDLS);
		default:
			(void) snprintf(servicemodebuf, sizeof(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) snprintf(stylebuf, sizeof(stylebuf), "unknown provider style 0x%x", style);
			return (stylebuf);
	}
}

char*
dlmactype(u_long 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);
		default:
			(void) snprintf(mediabuf, sizeof(mediabuf), "unknown media type 0x%x", media);
			return (mediabuf);
	}
}

/*VARARGS1*/
err(char *fmt, char *a1, char *a2, char *a3, char *a4)
{
	(void) fprintf(stderr, fmt, a1, a2, a3, a4);
	(void) fprintf(stderr, "\n");
	(void) exit(1);
}

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

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);
}