/*
 * jam 841031
 */

#include "../h/param.h"
#include "../h/mbuf.h"
#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../s32/kstat.h"
#include "../rpc/rpc_stat.h"

extern struct mbuf *getmbuf();

/* BUG the next comment is out of date */
/*
 * Allocate mbufs for size bytes of parameters.
 * The first contig must be contiguous (i.e.
 * must be in the first mbuf); the rest may
 * be divided into as many mbufs as deemed
 * necessary.
 */
caddr_t
rpc_allocParams(size)
   int size;
{
	register struct mbuf *m;

	RPC_INCMSTAT(paramPkts);
	if (!size)
		return NULL;
	m = getmbuf(size);
	RPC_ADDMSTAT(paramBytes,size);
	return(mtod(m, caddr_t));
}

rpc_freeParams(params)
   caddr_t params;
{
	RPC_DECMSTAT(paramPkts);
	if (!params)
		return;
	m_freem(dtom(params));
}

caddr_t
rpc_allocResults(size)
   int size;
{
	register struct mbuf *m;

	RPC_INCMSTAT(resultPkts);
	if (!size)
		return NULL;
	m = getmbuf(size);
	RPC_ADDMSTAT(resultBytes,size);
	return(mtod(m, caddr_t));
}

rpc_freeResults(results)
   caddr_t results;
{
	RPC_DECMSTAT(resultPkts);
	if (!results)
		return;
	m_freem(dtom(results));
}

rpc_header_t *
rpc_buildHeader(contents)
   caddr_t contents;
{
	register struct mbuf *m = dtom(contents);
	register rpc_header_t *pkt;

	/*
	 * If there is not enough room for the header
	 * in the first mbuf of the contents then we
	 * allocate a new mbuf.  We also leave as much
	 * room as possible at the beginning of the
	 * mbuf (for the data link layer).
	 */
	if (m == NULL ||
	    m->m_off > MMAXOFF || m->m_off < MMINOFF + sizeof(rpc_header_t)) {
		register struct mbuf *newm = m_get(M_DONTWAIT, MT_RPC);

		if (newm == 0)
			return(NULL);
		newm->m_next = m;
		newm->m_off = MMAXOFF - sizeof(rpc_header_t);
		newm->m_len = sizeof(rpc_header_t);
		m = newm;
	}

	/*
	 * There is enough room for the header so we
	 * use it.
	 */
	else {
		m->m_off -= sizeof(rpc_header_t);
		m->m_len += sizeof(rpc_header_t);
	}

	/*
	 * Fill in the fields that we can.
	 */
	pkt = mtod(m, rpc_header_t *);
	pkt->source = myNode;
	pkt->sourcegen = myGeneration;
	pkt->version = RPC_VERSION;
	return(pkt);
}

rpc_showPkt(pkt)
   rpc_header_t *pkt;
{
	printf("      source %x %06X-%06X, sourcegen 0x%X,",
		pkt->source.net, pkt->source.host.high, pkt->source.host.low,
		pkt->sourcegen);
	printf("      clientId 0x%X, seqno 0x%X, clientgen 0x%X\n",
		pkt->client, pkt->seqno, pkt->clientgen);
	printf("      type %d, class %d, op %d\n",
		pkt->type, pkt->class, pkt->operation);
}

rpc_freePkt(pkt)
   rpc_header_t *pkt;
{
	m_freem(dtom(pkt));
}

/*
 * Return a pointer to the first byte
 * of the contents of an RPC packet or
 * NULL if there are none.
 */
caddr_t
rpc_contents(pkt)
   rpc_header_t *pkt;
{
	register struct mbuf *m = dtom(pkt);
	register int offset;

	offset = (caddr_t)pkt - mtod(m, caddr_t) + sizeof(rpc_header_t);
	while (offset >= m->m_len) {
		offset -= m->m_len;
		if ((m = m->m_next) == NULL)
			return(NULL);
	}
	return(mtod(m, caddr_t) + offset);
}

/*
 * Discard the RPC header from an mbuf
 * chain and free any mbufs which become
 * empty.  Return a pointer to the next
 * byte of the packet or NULL if there are
 * no contents.
 */
caddr_t
rpc_discardHeader(pkt)
   rpc_header_t *pkt;
{
	register struct mbuf *m = dtom(pkt);
	register int offset;

	offset = (caddr_t)pkt - mtod(m, caddr_t) + sizeof(rpc_header_t);
	while (offset >= m->m_len) {
		offset -= m->m_len;
		if ((m = m_free(m)) == NULL)
			return(NULL);
	}
	return(mtod(m, caddr_t) + offset);
}

/*
 * Allocate a chain of mbufs such that
 * size bytes of data may be contained in
 * it.  Set m_off and m_len appropriately.
 */
struct mbuf *
getmbuf(size)
   register int size;
{
	struct mbuf *m0 = NULL;
	register struct mbuf * * mp = &m0;
	register struct mbuf * m;

	while (size) {
		MGET(m, M_WAIT, MT_RPC);
		*mp = m;
		mp = &m->m_next;
		m->m_off = MMINOFF;
		m->m_len = MIN(size, MLEN);
		size -= m->m_len;
	}
	m->m_next = NULL;
	return(m0);
}
