/*
 * Copyright (c) 1996-1999 by Sun Microsystems, Inc.
 * All rights reserved.
 *
 * heavily cannibalized from
 *
 * #ident	"@(#)dlprims.c	1.12	97/03/27 SMI"
 */

#pragma ident	"@(#)dlprims.c	1.1	99/04/09 SMI"

/*
 * TODO: get rid of this code as soon as possible and replace it with a
 *	 version from a standard library.  this stuff is barf-o-riffic.
 */

#include <errno.h>
#include <sys/types.h>
#include <sys/dlpi.h>
#include <stropts.h>
#include <sys/poll.h>

#include "dlpi_io.h"

static int strgetmsg(int, struct strbuf *, struct strbuf *);

/*
 * dlinforeq(): issue DL_INFO_REQ and fetch DL_INFO_ACK on stream
 *
 *   input: int: the stream to do the DL_INFO_REQ on
 *	    dl_info_ack_t: a place to store the DL_INFO_ACK
 *	    size_t: the size of the dl_info_ack_t
 *  output: int: 0 on success, 1 on failure (errno is set)
 */

int
dlinforeq(int fd, dl_info_ack_t *infoackp, size_t infoack_size)
{
	struct strbuf		ctl;

	infoackp->dl_primitive = DL_INFO_REQ;

	ctl.maxlen	= infoack_size;
	ctl.len		= DL_INFO_REQ_SIZE;
	ctl.buf		= (caddr_t)infoackp;

	if (putmsg(fd, &ctl, NULL, 0) == -1)
		return (1);

	if (strgetmsg(fd, &ctl, NULL) == 1)
		return (1);

	if (infoackp->dl_primitive != DL_INFO_ACK ||
	    ctl.len < DL_INFO_ACK_SIZE) {
		errno = EPROTO;
		return (1);
	}

	return (0);
}

/*
 * dlattachreq(): issue DL_ATTACH_REQ and fetch DL_OK_ACK on stream
 *
 *   input: int: the stream to do the DL_ATTACH_REQ on
 *	    t_uscalar_t: the ppa to do the attach to
 *  output: int: 0 on success, 1 on failure (errno is set)
 */

int
dlattachreq(int fd, t_uscalar_t ppa)
{
	union DL_primitives	*dlp;
	uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
	struct strbuf		ctl;

	dlp = (union DL_primitives *)buf;
	dlp->attach_req.dl_primitive	= DL_ATTACH_REQ;
	dlp->attach_req.dl_ppa		= ppa;

	ctl.maxlen	= sizeof (buf);
	ctl.len		= DL_ATTACH_REQ_SIZE;
	ctl.buf		= (caddr_t)dlp;

	if (putmsg(fd, &ctl, NULL, 0) == -1)
		return (1);

	if (strgetmsg(fd, &ctl, NULL) == 1)
		return (1);

	if (dlp->dl_primitive != DL_OK_ACK) {
		errno = EPROTO;
		return (1);
	}

	return (0);
}

/*
 * dlbindreq(): issue DL_BIND_REQ and fetch DL_BIND_ACK on stream
 *
 *   input: int: the stream to do the DL_BIND_REQ on
 *	    t_uscalar_t: the sap to bind to
 *	    t_uscalar_t: the max number of outstanding DL_CONNECT_IND messages
 *	    uint16_t: the service mode (connectionless/connection-oriented)
 *	    uint16_t: whether this is a connection management stream
 *  output: int: 0 on success, 1 on failure (errno is set)
 */

int
dlbindreq(int fd, t_uscalar_t sap, t_uscalar_t max_conind,
    uint16_t service_mode, uint16_t conn_mgmt)
{
	union DL_primitives	*dlp;
	uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
	struct strbuf		ctl;

	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	= sizeof (buf);
	ctl.len		= DL_BIND_REQ_SIZE;
	ctl.buf		= (caddr_t)dlp;

	if (putmsg(fd, &ctl, NULL, 0) == -1)
		return (1);

	if (strgetmsg(fd, &ctl, NULL) == 1)
		return (1);

	if (dlp->dl_primitive != DL_BIND_ACK || ctl.len < DL_BIND_ACK_SIZE) {
		errno = EPROTO;
		return (1);
	}

	return (0);
}

/*
 * strgetmsg(): timed getmsg(3C)
 *
 *   input: int: the stream to wait for the message on
 *	    struct strbuf *: a buffer to hold the control part of the message
 *	    struct strbuf *: a buffer to hold the data part of the message
 *  output: int: 0 on success, 1 on failure (errno is set)
 */

static int
strgetmsg(int fd, struct strbuf *ctlp, struct strbuf *datap)
{
	struct pollfd	fds;
	int		flags = 0;
	int		retval;

	fds.fd		= fd;
	fds.events	= POLLIN|POLLPRI;

	switch (poll(&fds, 1, DLPI_TIMEOUT * 1000)) {

	case 0:
		errno = ETIME;
		return (1);

	case -1:
		return (1);

	default:

		retval = getmsg(fd, ctlp, datap, &flags);
		if (retval == -1)
			return (1);

		if (retval > 0 || ctlp->len < sizeof (t_uscalar_t)) {
			errno = EPROTO;
			return (1);
		}

		break;
	}

	return (0);
}
