/*
 *	Copyright (c) 1995, 1996, Sun Microsystems, Inc.
 *
 * This software is provided by Sun ``AS IS'' and any express or implied
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose are disclaimed.
 * In no event shall Sun Microsystems be liable for any direct, indirect,
 * incidental, special, exemplary, or consequential damages.
 *
 * This software is not a product, and is provided for evaluation purposes
 * only.
 *
 * This software may not be resold without the express permission of
 * Sun Microsystems.
 *
 * Feedback on this tool may be sent to packet-shell-owner@sunroof.eng.sun.com.
 *
 * Packet Shell
 */
#pragma ident	"@(#)dlprims.c	1.10	98/11/12 SMI"

#ifdef SOLARIS
#include	<sys/types.h>
#include	<sys/stropts.h>
#include	<sys/signal.h>
#include	<sys/dlpi.h>
#include	<stdio.h>
#include	"dlprims.h"
#else /* SOLARIS */
/* XXX.sparker - stub dummies */
struct dlinforeq { char c; };
typedef int dl_info_ack_t;
typedef unsigned long ulong_t;
typedef unsigned short ushort_t;
#endif /* SOLARIS */

#define	DLMAXWAIT	(10)	/* max wait in seconds for response */
#define	DLMAXBUF	(256)

/*
 * Alarm timeout routine used internally.
 */
static	int	sigalrm();


/*
 * Issue DL_INFO_REQ and wait for DL_INFO_ACK.
 */
int
dlinforeq(int fd, dl_info_ack_t *infoackp, char **errp)
{
#ifdef SOLARIS
	union	DL_primitives	*dlp;
	char	buf[DLMAXBUF];
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->info_req.dl_primitive = DL_INFO_REQ;

	ctl.maxlen = DLMAXBUF;
	ctl.len = DL_INFO_REQ_SIZE;
	ctl.buf = (char *)dlp;

	flags = RS_HIPRI;

	if (putmsg(fd, &ctl, NULL, flags) < 0) {
		*errp = "dlinforeq:  putmsg";
		return (-1);
	}

	strgetmsg(fd, &ctl, NULL, &flags, "dlinfoack");
	expecting(DL_INFO_ACK, dlp, "dlinfoack");

	if (ctl.len < DL_INFO_ACK_SIZE) {
		*errp = "dlinfoack:  response ctl.len too short";
		return (-1);
	}

	if (flags != RS_HIPRI) {
		*errp = "dlinfoack:  DL_INFO_ACK was not M_PCPROTO";
		return (-1);
	}

	if (infoackp)
		*infoackp = dlp->info_ack;
#endif /* SOLARIS */
	return (0);
}

/*
 * Issue DL_ATTACH_REQ.
 * Return zero on success, nonzero on error.
 */
void
dlattachreq(int fd, ulong_t ppa)
{
#ifdef SOLARIS
	union	DL_primitives	*dlp;
	char	buf[DLMAXBUF];
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->attach_req.dl_primitive = DL_ATTACH_REQ;
	dlp->attach_req.dl_ppa = ppa;

	ctl.maxlen = DLMAXBUF;
	ctl.len = DL_ATTACH_REQ_SIZE;
	ctl.buf = (char *)dlp;

	flags = 0;

	if (putmsg(fd, &ctl, NULL, flags) < 0) {
		(void) fprintf(stderr, "dlattachreq:  putmsg");
	}

	strgetmsg(fd, &ctl, NULL, &flags, "dlattachreq");
	expecting(DL_OK_ACK, dlp, "dlattachreq");
#endif /* SOLARIS */
}

/*
 * Issue DL_PROMISCON_REQ and wait for DL_OK_ACK.
 */
int
dlpromiscon(int fd, int level)
{
#ifdef SOLARIS
	union	DL_primitives	*dlp;
	char	buf[DLMAXBUF];
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->promiscon_req.dl_primitive = DL_PROMISCON_REQ;
	dlp->promiscon_req.dl_level = level;

	ctl.maxlen = DLMAXBUF;
	ctl.len = DL_PROMISCON_REQ_SIZE;
	ctl.buf = (char *)dlp;

	flags = 0;

	if (putmsg(fd, &ctl, NULL, flags) < 0) {
		(void) fprintf(stderr, "dlpromiscon:  putmsg\n");
		return (-1);
	}

	strgetmsg(fd, &ctl, NULL, &flags, "dlpromisconreq");
	expecting(DL_OK_ACK, dlp, "dlpromisconreq");
#endif /* SOLARIS */
	return (0);
}

/*
 * Issue DL_PROMISCOFF_REQ and wait for DL_OK_ACK.
 */
dlpromiscoff(int fd, int level)
{
#ifdef SOLARIS
	union	DL_primitives	*dlp;
	char	buf[DLMAXBUF];
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->promiscoff_req.dl_primitive = DL_PROMISCON_REQ;
	dlp->promiscoff_req.dl_level = level;

	ctl.maxlen = DLMAXBUF;
	ctl.len = DL_PROMISCOFF_REQ_SIZE;
	ctl.buf = (char *)dlp;

	flags = 0;

	if (putmsg(fd, &ctl, NULL, flags) < 0) {
		(void) fprintf(stderr, "dlpromiscoff:  putmsg\n");
		return (-1);
	}

	strgetmsg(fd, &ctl, NULL, &flags, "dlpromiscoffreq");
	expecting(DL_OK_ACK, dlp, "dlpromiscoffreq");
#endif /* SOLARIS */
	return (0);
}
#if 0 /* unported */

dlenabmulti(int fd, char *addrp, int len)
{
	char	buf[DLMAXBUF];
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->dl_primitive = DL_ENABMULTI_REQ;
	dlp->enabmulti_req.dl_addr_length = len;
	dlp->enabmulti_req.dl_addr_offset = DL_ENABMULTI_REQ_SIZE;
	(void) memcpy((caddr_t)(buf + DL_ENABMULTI_REQ_SIZE), addrp, len);

	ctl.maxlen = sizeof (*dlp);
	ctl.len = DL_ENABMULTI_REQ + len;
	ctl.buf = buf;

	flags = 0;

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

	strgetmsg(fd, &ctl, NULL, &flags, "dlenabmultireq");
	expecting(DL_OK_ACK, dlp, "dlenabmultireq");
}

dldisabmulti(int fd, char *addrp, int len)
{
	char	buf[DLMAXBUF];
	union	DL_primitives	*dlp;
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->dl_primitive = DL_DISABMULTI_REQ;
	dlp->enabmulti_req.dl_addr_length = len;
	dlp->enabmulti_req.dl_addr_offset = DL_DISABMULTI_REQ_SIZE;
	(void) memcpy((caddr_t)(buf + DL_DISABMULTI_REQ_SIZE), addrp, len);

	ctl.maxlen = sizeof (*dlp);
	ctl.len = DL_DISABMULTI_REQ + len;
	ctl.buf = buf;

	flags = 0;

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

	strgetmsg(fd, &ctl, NULL, &flags, "dldisabmultireq");
	expecting(DL_OK_ACK, dlp, "dldisabmultireq");
}
#endif /* 0 */

int
dlbindreq(int fd, ulong_t sap, ulong_t max_conind, ushort_t service_mode,
    ushort_t conn_mgmt)
{
#ifdef SOLARIS
	union	DL_primitives	*dlp;
	char	buf[DLMAXBUF];
	struct	strbuf	ctl;
	int	flags;

	dlp = (union DL_primitives *)buf;

	dlp->bind_req.dl_primitive = DL_BIND_REQ;
	dlp->bind_req.dl_sap = sap;
	dlp->bind_req.dl_max_conind = max_conind;
	dlp->bind_req.dl_service_mode = service_mode;
	dlp->bind_req.dl_conn_mgmt = conn_mgmt;
	dlp->bind_req.dl_xidtest_flg = 0;

	ctl.maxlen = DLMAXBUF;
	ctl.len = DL_BIND_REQ_SIZE;
	ctl.buf = (char *)dlp;

	flags = 0;

	if (putmsg(fd, &ctl, NULL, flags) < 0) {
		(void) fprintf(stderr, "dlbindreq:  putmsg\n");
		return (-1);
	}

	ctl.len = 0;

	strgetmsg(fd, &ctl, NULL, &flags, "dlbindack");
	expecting(DL_BIND_ACK, dlp, "dlbindack");

	if (ctl.len < sizeof (DL_BIND_ACK_SIZE)) {
		(void) fprintf(stderr,
		    "dlbindack:  response ctl.len too short:  %d\n", ctl.len);
		return (-1);
	}
#endif /* SOLARIS */
	return (0);
}

#ifdef SOLARIS
static int
sigalrm()
{
	(void) fprintf(stderr, "sigalrm:  TIMEOUT\n");
}

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

	/*
	 * Start timer.
	 */
	signal(SIGALRM, sigalrm);
	if (alarm(DLMAXWAIT) < 0) {
		(void) sprintf(errmsg, "%s:  alarm", caller);
		(void) fprintf(stderr, errmsg);
	}

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

	/*
	 * Stop timer.
	 */
	if (alarm(0) < 0) {
		(void) sprintf(errmsg, "%s:  alarm", caller);
		(void) fprintf(stderr, errmsg);
	}

	/*
	 * Check for MOREDATA and/or MORECTL.
	 */
	if ((rc & (MORECTL | MOREDATA)) == (MORECTL | MOREDATA)) {
		(void) fprintf(stderr, "%s:  strgetmsg:  MORECTL|MOREDATA",
		    caller);
	}
	if (rc & MORECTL) {
		(void) fprintf(stderr, "%s:  strgetmsg:  MORECTL", caller);
	}
	if (rc & MOREDATA) {
		(void) fprintf(stderr, "%s:  strgetmsg:  MOREDATA", caller);
	}

	/*
	 * Check for at least sizeof (long) control data portion.
	 */
	if (ctlp->len < sizeof (long)) {
		(void) fprintf(stderr,
		    "%s:  control portion length < sizeof (long)", caller);
	}
}

int
expecting(ulong_t prim, union DL_primitives *dlp, char *caller)
{
	if (dlp->dl_primitive == DL_ERROR_ACK) {
		(void) fprintf(stderr, "%s:  DL_ERROR_ACK:  dl_errno %d "
		    "unix_errno %d\n", caller, dlp->error_ack.dl_errno,
		    dlp->error_ack.dl_unix_errno);
		return (1);
	}

	if (dlp->dl_primitive != prim) {
		(void) fprintf(stderr,
		    "%s:  unexpected primitive 0x%x received\n", caller,
		    dlp->dl_primitive);
		return (1);
	}

	return (0);
}
#endif /* SOLARIS */
