/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1990 MIPS Computer Systems, Inc.            |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 52.227-7013.   |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Drive                                |
 * |         Sunnyvale, CA 94086                               |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: dlpilib.c,v 1.1.2.1 92/06/24 14:12:25 marker Exp $"

#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/dlpi.h>
#include <sys/stropts.h>
#include <svr3/bsd/net/if.h>
#include <sys/dlpisif.h>
#include <poll.h>

char databuff[10240];
char ctrlbuff[256];
struct strbuf ctl;
struct strbuf dat;
int datalen;
char fromaddr[256];
char toaddr[256];
int fromaddrlen, toaddrlen;
int dlpi_fd;
int ret;
int flag = 0;
int display_data = 0;
int verbose = 0;
extern char *sys_errlist[];
dl_info_ack_t dlpi_last_info;
unsigned char dlpi_macaddr[256];
int dlpi_macaddr_len;


#ifndef	PHYAD_SIZE
#define	PHYAD_SIZE	6
#endif

#define	verb_printf	if (verbose) printf
#define	fverb_printf	if (verbose) fprintf

dlpi_open(filename)
	char *filename;
{
	int fd;
	char msg[256];

	verb_printf("*** open (%s)\n", filename);
	if ((fd = open(filename, O_RDWR)) < 0) {
		sprintf (msg, "open %s failed", filename);
		perror (msg);
		exit (1);
	}
	dlpi_fd = fd;
	return fd;
}


dlpi_strioctl(fd, cmd, timout, dp, len)
	int fd, cmd, timout;
	char *dp;
	int len;
{
	struct strioctl strioc;
	char msg[256];

	verb_printf("*** strioctl\n");
	strioc.ic_cmd    = cmd;
	strioc.ic_timout = timout;
	strioc.ic_dp     = dp;
	strioc.ic_len    = len;
	if (ioctl(fd, I_STR, &strioc) < 0) {
		sprintf (msg, "stream ioctl (cmd:%x) failed", cmd);
		perror (msg);
		exit (2);
	}
}

dlpi_info_req(fd)
	int fd;
{
	dl_info_req_t info;
	struct strbuf ctl;
	struct strbuf dat;

	verb_printf("*** DL_INFO_REQ\n");
	ctl.buf = (char *)&info;
	ctl.maxlen = ctl.len = sizeof(info);

	dat.buf = NULL;
	dat.len = -1;
	dat.maxlen = 0;

	info.dl_primitive = DL_INFO_REQ;
	if (dlpi_putmsg(fd, &ctl, &dat, 0) < 0) {
		perror("dlpi_putmsg for DL_INFO_REQ failed");
		exit(3);
	}
	dlpi_get(fd);		/* get DL_INFO_ACK */
}

dlpi_attach(fd, ppa)
	int fd, ppa;
{
	dl_attach_req_t attach;
	struct strbuf ctl;
	struct strbuf dat;

	verb_printf("*** DL_ATTACH_REQ PPA: %d\n", ppa);
	ctl.buf = (char *)&attach;
	ctl.maxlen = ctl.len = sizeof(attach);

	dat.buf = NULL;
	dat.len = -1;
	dat.maxlen = 0;

	attach.dl_primitive = DL_ATTACH_REQ;
	attach.dl_ppa       = ppa;
	if (dlpi_putmsg(fd, &ctl, &dat, 0) < 0) {
		perror("dlpi_putmsg for DL_ATTACH_REQ failed");
		exit(4);
	}
	dlpi_get(fd);		/* get DL_OK_ACK */
}

dlpi_detach(fd)
	int fd;
{
	dl_detach_req_t detach;
	struct strbuf ctl;
	struct strbuf dat;

	verb_printf("*** DL_DETACH_REQ\n");
	ctl.buf = (char *)&detach;
	ctl.maxlen = ctl.len = sizeof(detach);

	dat.buf = NULL;
	dat.len = -1;
	dat.maxlen = 0;

	detach.dl_primitive = DL_DETACH_REQ;
	if (dlpi_putmsg(fd, &ctl, &dat, 0) < 0) {
		perror("dlpi_putmsg for DL_DETACH_REQ failed");
		exit(4);
	}
	dlpi_get(fd);		/* get DL_OK_ACK */
}

dlpi_bind(fd, sap, max_conind, service_mode)
	int fd, sap, max_conind, service_mode;
{
	dl_bind_req_t bind;
	struct strbuf ctl;
	struct strbuf dat;

	verb_printf("*** DL_BIND_REQ SAP: %d (0x%x)\n", sap, sap);
	ctl.buf = (char *)&bind;
	ctl.maxlen = ctl.len = sizeof(bind);

	dat.buf = NULL;
	dat.len = -1;
	dat.maxlen = 0;

	bind.dl_primitive = DL_BIND_REQ;
	bind.dl_sap = sap;
	bind.dl_max_conind = max_conind;
	bind.dl_service_mode = service_mode;
	if (dlpi_putmsg(fd, &ctl, &dat, 0) < 0) {
		perror("dlpi_putmsg for DL_BIND_REQ failed");
		exit(5);
	}
	dlpi_get(fd);		/* get DL_BIND_ACK */
}


dlpi_send(fd, dest, destlen, data, datalen)
	int fd;
	char *dest;
	int destlen;
	char *data;
	int datalen;
{
	register dl_unitdata_req_t *udp;
	char ctrlbuff[256];
	struct strbuf ctl;
	struct strbuf dat;

	verb_printf("*** UNITDATA_REQ LEN: %d TO ", datalen);
	dlpi_enetaddr (dest, destlen);
	dat.buf    = data;
	dat.maxlen = dat.len = datalen;
	ctl.buf    = ctrlbuff;
	ctl.len    = DL_UNITDATA_REQ_SIZE + PHYAD_SIZE + 1;
	ctl.maxlen = sizeof(ctrlbuff);

	udp = (dl_unitdata_req_t *)ctrlbuff;
	udp->dl_primitive        = DL_UNITDATA_REQ;
	udp->dl_dest_addr_length = destlen;
	udp->dl_dest_addr_offset = DL_UNITDATA_REQ_SIZE;
	memcpy(ctrlbuff + DL_UNITDATA_REQ_SIZE, dest, destlen);

	if (display_data)
	    printf("sending string\t<%s>\n", data);

	if (dlpi_putmsg(fd, &ctl, &dat, 0) < 0) {
		perror("dlpi_putmsg for DL_UNITDATA_REQ failed");
		exit(6);
	}
}

dlpi_message(fd)
	int fd;
{
	struct pollfd pollfds[1];

	pollfds[0].fd = fd;
	pollfds[0].events = POLLIN|POLLPRI;
	poll(pollfds, 1, 0);
	return pollfds[0].revents & (POLLIN|POLLPRI);
}

char *errors[] = {
"DL_BADSAP (0x00) Bad LSAP selector",
"DL_BADADDR (0x01) DLSAP address in improper format or invalid",
"DL_ACCESS (0x02) Improper permissions for request",
"DL_OUTSTATE (0x03) Primitive issued in improper state",
"DL_SYSERR (0x04) UNIX system error occurred",
"DL_BADCORR (0x05) Sequence number not from outstanding DL_CONN_IND",
"DL_BADDATA (0x06) User data exceeded provider limit",
"DL_UNSUPPORTED (0x07) Requested service not supplied by provider",
"DL_BADPPA (0x08) Specified PPA was invalid",
"DL_BADPRIM (0x09) Primitive received is not known by DLS provider",
"DL_BADQOSPARAM (0x0a) QOS parameters contained invalid values",
"DL_BADQOSTYPE (0x0b) QOS structure type is unknown or unsupported",
"DL_BADTOKEN (0x0c) Token used not associated with an active stream",
"DL_BOUND (0x0d) Attempted second bind with dl_max_conind or \n\
 (dl_conn_mgmt) > 0 on same DLSAP or PPA",
"DL_INITFAILED (0x0e) Physical Link initialization failed",
"DL_NOADDR (0x0f) Provider couldn't allocate alternate address",
"DL_NOTINIT (0x10) Physical Link not initialized",
"DL_UNDELIVERABLE (0x11) Previous data unit could not be delivered",
"DL_NOTSUPPORTED (0x12) Primitive is known but not supported by DLS provider"
};
#define ERR_MAX 0x12

char *prims[] = {
"DL_INFO_REQ (0x00) Information Req",
"DL_BIND_REQ (0x01) Bind dlsap address",
"DL_UNBIND_REQ (0x02) Unbind dlsap address",
"DL_INFO_ACK (0x03) Information Ack",
"DL_BIND_ACK (0x04) Dlsap address bound",
"DL_ERROR_ACK (0x05) Error acknowledgment",
"DL_OK_ACK (0x06) Success acknowledgment",
"DL_UNITDATA_REQ (0x07) datagram send request",
"DL_UNITDATA_IND (0x08) datagram receive indication",
"DL_UDERROR_IND (0x09) datagram error indication",
"DL_UDQOS_REQ (0x0a) set QOS for subsequent datagram transmissions",
"DL_ATTACH_REQ (0x0b) Attach a PPA",
"DL_DETACH_REQ (0x0c) Detach a PPA",
"DL_CONNECT_REQ (0x0d) Connect request",
"DL_CONNECT_IND (0x0e) Incoming connect indication",
"DL_CONNECT_RES (0x0f) Accept previous connect indication",
"DL_CONNECT_CON (0x10) Connection established",
"DL_TOKEN_REQ (0x11) Passoff token request",
"DL_TOKEN_ACK (0x12) Passoff token ack",
"DL_DISCONNECT_REQ (0x13) Disconnect request",
"DL_DISCONNECT_IND (0x14) Disconnect indication",
"0x15 (unknown",
"0x16 (unknown",
"DL_RESET_REQ (0x17) Reset service request",
"DL_RESET_IND (0x18) Incoming reset indication",
"DL_RESET_RES (0x19) Complete reset processing",
"DL_RESET_CON (0x1a) Reset processing complete",
"DL_SUBS_BIND_REQ (0x1b) Bind Subsequent DLSAP address",
"DL_SUBS_BIND_ACK (0x1c) Subsequent DLSAP address bound"
};
#define PRIM_MAX 0x1c

char *states[] = {
"DL_UNBOUND (0x00) PPA attached",
"DL_BIND_PENDING (0x01) Waiting ack of DL_BIND_REQ",
"DL_UNBIND_PENDING (0x02) Waiting ack of DL_UNBIND_REQ",
"DL_IDLE (0x03) dlsap bound, awaiting use",
"DL_UNATTACHED (0x04) PPA not attached",
"DL_ATTACH_PENDING (0x05) Waiting ack of DL_ATTACH_REQ",
"DL_DETACH_PENDING (0x06) Waiting ack of DL_DETACH_REQ",
"DL_UDQOS_PENDING (0x07) Waiting ack of DL_UDQOS_REQ",
"DL_OUTCON_PENDING (0x08) outgoing connection, awaiting DL_CONN_CON",
"DL_INCON_PENDING (0x09) incoming connection, awaiting DL_CONN_RES",
"DL_CONN_RES_PENDING (0x0a) Waiting ack of DL_CONNECT_RES",
"DL_DATAXFER (0x0b) connection-oriented data transfer",
"DL_USER_RESET_PENDING (0x0c) user initiated reset, awaiting DL_RESET_CON",
"DL_PROV_RESET_PENDING (0x0d) provider initiated reset, awaiting DL_RESET_RES",
"DL_RESET_RES_PENDING (0x0e) Waiting ack of DL_RESET_RES",
"DL_DISCON8_PENDING (0x0f) Waiting ack of DL_DISC_REQ when in DL_OUTCON_PENDING",
"DL_DISCON9_PENDING (0x10) Waiting ack of DL_DISC_REQ when in DL_INCON_PENDING",
"DL_DISCON11_PENDING (0x11) Waiting ack of DL_DISC_REQ when in DL_DATAXFER",
"DL_DISCON12_PENDING (0x12) Waiting ack of DL_DISC_REQ when in DL_USER_RESET_PENDING",
"DL_DISCON13_PENDING (0x13) Waiting ack of DL_DISC_REQ when in DL_DL_PROV_RESET_PENDING",
"DL_SUBS_BIND_PND (0x14) Waiting ack of DL_SUBS_BIND_REQ"
};
#define STATE_MAX 0x14

char *media[] = {
"DL_CSMACD (0x0) IEEE 802.3 CSMA/CD network",
"DL_TPB (0x1) IEEE 802.4 Token Passing Bus",
"DL_TPR (0x2) IEEE 802.5 Token Passing Ring",
"DL_METRO (0x3) IEEE 802.6 Metro Net",
"DL_ETHER (0x4) Ethernet Bus",
"DL_HDLC (0x05) ISO HDLC protocol support, bit synchronous",
"DL_CHAR (0x06) Character Synchronous protocol support, eg BISYNC",
"DL_CTCA (0x07) IBM Channel-to-Channel Adapter"
};
#define	MEDIA_MAX	0x07

dlpi_get(fd)
	int fd;
{
	union DL_primitives *p;
	int i;

	dat.buf    = databuff;
	dat.len    = 0;
	dat.maxlen = sizeof(databuff);
	ctl.buf    = ctrlbuff;
	ctl.len    = 0;
	ctl.maxlen = sizeof(ctrlbuff);

	for (i = 0; i < sizeof (databuff); i++)
	    databuff[i] = 0;

	verb_printf("*** GET MSG\n");
	ret = getmsg(fd, &ctl, &dat, &flag);
	if (ret < 0) {
		perror("getmsg failed");
		exit(7);
	}
	if (ctl.len <= 0) {
		fverb_printf(stderr, "ctl.len = %d\n", ctl.len);
		exit(8);
	}
	p = (union DL_primitives *)ctl.buf;
	switch (p->dl_primitive) {
	    case DL_BIND_ACK:
		verb_printf("*** DL_BIND_ACK\n");
		break;

	    case DL_ERROR_ACK:
	    {
		dl_error_ack_t *ea = (dl_error_ack_t *)ctrlbuff;
		verb_printf("*** DL_ERROR_ACK\n");
		verb_printf("\terror primitive:\t0x%x: %s\n", ea->dl_error_primitive,
		    ea->dl_error_primitive <= PRIM_MAX ?
		    prims[ea->dl_error_primitive] : "UNKNOWN");

		verb_printf("\terror code:\t\t0x%x: %s\n", ea->dl_errno, 
		    ea->dl_errno <= ERR_MAX ?
		    errors[ea->dl_errno] : "UNKNOWN");

		verb_printf("\tunix error code:\t0x%x: %s\n", ea->dl_unix_errno,
		    sys_errlist[ea->dl_unix_errno]);

		dlpi_info_req(fd);		/* update status */
		exit (9);
		break;
	    }

	    case DL_OK_ACK:
	    {
		dl_ok_ack_t *oa = (dl_ok_ack_t *)ctrlbuff;

		verb_printf("*** DL_OK_ACK\n");
		verb_printf("\tcorrect primitive:\t0x%x: %s\n",
		    oa->dl_correct_primitive,
		    oa->dl_correct_primitive <= PRIM_MAX ?
			prims[oa->dl_correct_primitive] : "UNKNOWN");
		break;
	    }

	    case DL_UNITDATA_IND:
	    {
		dl_unitdata_ind_t *udi = (dl_unitdata_ind_t *)ctrlbuff;
		char *cp;

		verb_printf("*** DL_UNITDATA_IND  LEN: %d\n", dat.len);
		datalen = dat.len;

		cp = (caddr_t) udi + udi->dl_dest_addr_offset;
		toaddrlen = udi->dl_dest_addr_length;
		verb_printf("sent to: ");
		for (i = 0; i < toaddrlen; i++) {
		    verb_printf ("%x:", *cp);
		    toaddr[i] = *cp++;
		}

		cp = (caddr_t) udi + udi->dl_src_addr_offset;
		fromaddrlen = udi->dl_src_addr_length;
		verb_printf("  received from: ");
		for (i = 0; i < fromaddrlen; i++) {
		    verb_printf ("%x:", *cp);
		    fromaddr[i] = *cp++;
		}

		if (display_data) {
		    verb_printf(" string :<%s>\n", databuff);
		    for (i = 0; i < dat.len; i++)
			verb_printf ("%x ", databuff[i]);
		}
		verb_printf ("\n");
		return 1;
	    }

	    case DL_UDERROR_IND:
	    {
		dl_uderror_ind_t *ude = (dl_uderror_ind_t *)ctrlbuff;
		verb_printf("*** DL_UDERROR_IND\n");
		verb_printf("error = %d\n", ude->dl_errno);
		exit (10);
	    }

	    case DL_INFO_ACK:
	    {
		dl_info_ack_t *ia = (dl_info_ack_t *)ctrlbuff;

		dlpi_last_info = *ia;		/* save last info */
		verb_printf("*** DL_INFO_ACK\n");
		verb_printf("\tmax_sdu\t\t\t%d\n",		ia->dl_max_sdu);
		verb_printf("\tmin_sdu\t\t\t%d\n",		ia->dl_min_sdu);
		verb_printf("\tmac_type\t\t%d - %s\n",	ia->dl_mac_type,
			ia->dl_mac_type <= MEDIA_MAX ?
			media[ia->dl_mac_type] : "UNKNOWN");

		verb_printf("\treserved\t\t%d\n",		ia->dl_reserved);
		verb_printf("\tcurrent_state\t\t%d - %s\n",	ia->dl_current_state,
			ia->dl_current_state <= STATE_MAX ?
			states[ia->dl_current_state] : "UNKNOWN");

		verb_printf("\tmax_idu\t\t\t%d\n",		ia->dl_max_idu);
		verb_printf("\tservice_mode\t\t%d - %s & %s\n", ia->dl_service_mode,
			ia->dl_service_mode & DL_CODLS ? "Connection" : "-",
			ia->dl_service_mode & DL_CLDLS ? "Connectionless" : "-");

		verb_printf("\tqos_length\t\t%d\n",		ia->dl_qos_length);
		verb_printf("\tqos_offset\t\t%d\n",		ia->dl_qos_offset);
		verb_printf("\tqos_range_length\t%d\n",     ia->dl_qos_range_length);
		verb_printf("\tqos_range_offset\t%d\n",     ia->dl_qos_range_offset);
		verb_printf("\tprovider_style\t\t0x%x - %s\n", ia->dl_provider_style,
			ia->dl_provider_style == DL_STYLE1 ? "Style 1" : 
			ia->dl_provider_style == DL_STYLE2 ? "Style 2" :
			"UNKNOWN");

		verb_printf("\taddr_offset\t\t%d\n",		ia->dl_addr_offset);
		verb_printf("\taddr_length\t\t%d\n",		ia->dl_addr_length);
		verb_printf("\tgrowth\t\t\t%d\n",		ia->dl_growth);
		dlpi_printaddr (ia, ia->dl_addr_offset, ia->dl_addr_length);
		fflush (stdout);
		break;
	    }

	    default:
		fverb_printf(stderr,
		    "dlpit: unknown primitive 0x%x: %s\n",
			p->dl_primitive,
			p->dl_primitive <= PRIM_MAX ?
			    prims[p->dl_primitive] : "UNKNOWN");
		exit(11);
	}
	/* sleep(2); */
	return 0;
} 

dlpi_printaddr(base, offset, length)
	char *base;
	int offset;
	int length;
{
	if (offset && length) {
	    if (verbose) {
		printf("\tmac address:\t\t");
		dlpi_enetaddr (&base[offset], length);
	    }

	    /* save mac address for later use */
	    memcpy (dlpi_macaddr, &base[offset], length);
	    dlpi_macaddr_len = length;
	}
}

dlpi_enetaddr (addr, length)
	unsigned char *addr;
{
	register len;
	for (len = 1; len <= length; len++)
	    printf ("%x%s", *addr++,
		len < 6      ? ":" :
		len < length ? "." : "");

	printf ("\n");
}

dlpi_getenet(enet_inpb, ea)
	char *enet_inpb;	/* input ascii ethernet adrs */
	unsigned char ea[];	/* output binary ethernet address */
{
	int a, b, c, d, e, f;
	sscanf (enet_inpb, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
	    ea[0] = a; ea[1] = b;
	    ea[2] = c; ea[3] = d;
	    ea[4] = e; ea[5] = f;
}

dlpi_putmsg(fd, ctl, dat, flag)
	int fd;
	char *ctl;
	char *dat;
	int flag;
{
	fflush (stdout);
	fflush (stderr);
	putmsg (fd, ctl, dat, flag);
}

/* === */
