/*
 * DLPI support routines to handle PPP.
 */

#include	<sys/types.h>
#include	<sys/stream.h>
#include	<sys/stropts.h>
#include	<sys/dlpi.h>

#include	<syslog.h>

#include	"ppp.h"

#ifdef	__STDC__
char *dlprim(u_long);
#else
char *dlprim(/* u_long */);
#endif

dlattachreq(fd, ppa)
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;

    return putmsg(fd, &ctl, (struct strbuf*) NULL, flags);
}

dlbindreq(fd, sap, max_conind, service_mode, conn_mgmt, xidtest)
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;

    return putmsg(fd, &ctl, (struct strbuf*) NULL, flags);
}

dlsubsbindreq(fd, sap_addr, sap_length, bind_class)
int	fd;
u_long	*sap_addr;
u_long	sap_length;
u_long	bind_class;
{
    struct	strbuf	ctl;
    int	flags;
    char buf[DP_MAXDLBUF];
    dl_subs_bind_req_t	*subs_bind_req = (dl_subs_bind_req_t *)buf;

    subs_bind_req->dl_primitive = DL_SUBS_BIND_REQ;
    subs_bind_req->dl_subs_sap_offset = sizeof(dl_subs_bind_req_t);
    subs_bind_req->dl_subs_sap_length = sap_length;
    subs_bind_req->dl_subs_bind_class = bind_class;

    memcpy(((char *)subs_bind_req) + sizeof(dl_subs_bind_req_t),
	   (char *)sap_addr, sap_length);

    ctl.maxlen = 0;
    ctl.len = sizeof(dl_subs_bind_req_t) + sap_length;
    ctl.buf = (char *)subs_bind_req;

    flags = 0;

    return putmsg(fd, &ctl, (struct strbuf*) NULL, flags);
}

dlunitdatareq(fd, addrp, addrlen, minpri, maxpri, datap, datalen)
int	fd;
u_char	*addrp;
int	addrlen;
u_long	minpri, maxpri;
u_char	*datap;
int	datalen;
{
    long	buf[DP_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(((char *)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;

    return putmsg(fd, &ctl, &data, 0);
}

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

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

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

    dlp = (union DL_primitives *) ctl.buf;

    if (expecting(DL_OK_ACK, dlp) < 0)
	return -1;

    if (ctl.len < sizeof (dl_ok_ack_t)) {
	syslog(LOG_ERR, "dlokack: respons ctl.len to short: %d", ctl.len);
	return -1;
    }

    if (flags != RS_HIPRI) {
	syslog(LOG_ERR, "dlokack: DL_OK_ACK was not M_PCPROTO");
	return -1;
    }

    if (ctl.len < sizeof (dl_ok_ack_t)) {
	syslog(LOG_ERR, "dlokack: short response ctl.len:  %d", ctl.len);
	return -1;
    }
    return 0;
}


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

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

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

    dlp = (union DL_primitives *) ctl.buf;

    if (expecting(DL_BIND_ACK, dlp) < 0)
	return -1;

    if (flags != RS_HIPRI) {
	syslog(LOG_ERR, "dlbinack: DL_OK_ACK was not M_PCPROTO");
	return -1;
    }

    if (ctl.len < sizeof (dl_bind_ack_t)) {
	syslog(LOG_ERR, "dlbinack: short response ctl.len:  %d", ctl.len);
	return -1;
    }
    return 0;
}

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

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

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

    dlp = (union DL_primitives *)ctl.buf;

    if (expecting(DL_SUBS_BIND_ACK, dlp) < 0)
	return -1;

    if (flags != RS_HIPRI) {
	syslog(LOG_ERR, "dlsubsbindack: DL_OK_ACK was not M_PCPROTO");
	return -1;
    }

    if (ctl.len < sizeof (dl_subs_bind_ack_t)) {
	syslog(LOG_ERR, "dlsubsbindack: short response ctl.len:  %d", ctl.len);
	return -1;
    }
    return 0;
}

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

#if	0
    /*
     * Start timer.
     */
    (void) signal(SIGALRM, sigalrm);
    if (alarm(DP_MAXWAIT) < 0) {
	syslog(LOG_ERR, "%s: alarm, %m", caller);
	return -1;
    }
#endif

    /*
     * Set flags argument and issue getmsg().
     */
    *flagsp = 0;
    if ((rc = getmsg(fd, ctlp, datap, flagsp)) < 0) {
#if	0
	syslog(LOG_ERR, "%s: getmsg failed, %m", caller);
#endif
	return -1;
    }

#if	0
    /*
     * Stop timer.
     */
    if (alarm(0) < 0) {
	syslog(LOG_ERR, "%s: alarm, %m", caller);
	return -1;
    }
#endif

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

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

expecting(prim, dlp)
int	prim;
union	DL_primitives	*dlp;
{
    if (dlp->dl_primitive != (u_long)prim) {
	syslog(LOG_ERR, "expected %s got %s", dlprim(prim),
	       dlprim(dlp->dl_primitive));
	return -1;
    }
    return 0;
}

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

    switch ((int)prim) {
     case DL_INFO_REQ:		return "DL_INFO_REQ";		break;
     case DL_INFO_ACK:		return "DL_INFO_ACK";		break;
     case DL_ATTACH_REQ:	return "DL_ATTACH_REQ";		break;
     case DL_DETACH_REQ:	return "DL_DETACH_REQ";		break;
     case DL_BIND_REQ:		return "DL_BIND_REQ";		break;
     case DL_BIND_ACK:		return "DL_BIND_ACK";		break;
     case DL_UNBIND_REQ:	return "DL_UNBIND_REQ";		break;
     case DL_OK_ACK:		return "DL_OK_ACK";		break;
     case DL_ERROR_ACK:		return "DL_ERROR_ACK";		break;
     case DL_SUBS_BIND_REQ:	return "DL_SUBS_BIND_REQ";	break;
     case DL_SUBS_BIND_ACK:	return "DL_SUBS_BIND_ACK";	break;
     case DL_UNITDATA_REQ:	return "DL_UNITDATA_REQ";	break;
     case DL_UNITDATA_IND:	return "DL_UNITDATA_IND";	break;
     case DL_UDERROR_IND:	return "DL_UDERROR_IND";	break;
     case DL_UDQOS_REQ:		return "DL_UDQOS_REQ";		break;
     case DL_CONNECT_REQ:	return "DL_CONNECT_REQ";	break;
     case DL_CONNECT_IND:	return "DL_CONNECT_IND";	break;
     case DL_CONNECT_RES:	return "DL_CONNECT_RES";	break;
     case DL_CONNECT_CON:	return "DL_CONNECT_CON";	break;
     case DL_TOKEN_REQ:		return "DL_TOKEN_REQ";		break;
     case DL_TOKEN_ACK:		return "DL_TOKEN_ACK";		break;
     case DL_DISCONNECT_REQ:	return "DL_DISCONNECT_REQ";	break;
     case DL_DISCONNECT_IND:	return "DL_DISCONNECT_IND";	break;
     case DL_RESET_REQ:		return "DL_RESET_REQ";		break;
     case DL_RESET_IND:		return "DL_RESET_IND";		break;
     case DL_RESET_RES:		return "DL_RESET_RES";		break;
     case DL_RESET_CON:		return "DL_RESET_CON";		break;
     default:			(void) sprintf(primbuf,
					       "unknown primitive 0x%x", prim);
				return primbuf;
    }
}

strioctl(fd, cmd, timout, len, dp)
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);
}
