#include "../h/param.h"
#include "../h/systm.h"			/* C_WAIT */
#include "../h/errno.h"
#include "../h/mbuf.h"
#include "../h/socket.h"
#include "../h/time.h"
#include "../h/kernel.h"
#include "../net/if.h"
#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../vnet/bulkdata.h"
#define BULK_STAT_INIT
#include "../s32/kstat.h"
#include "../vnet/bulkdata_stat.h"

#include "../h/ioctl.h"
#include "../efs/efs_ioctl.h"

extern struct mbuf *getmbuf();
extern connection_t *conn_findByAddress();

#define HASHSIZE	32		/* Size of hash table */

/*
 * Tunable constants.  These are the default
 * values for the variables with similar names.
 */
#define MAXPKTDATA	BULK_MAXPKTDATA	/* Maximum data bytes per send packet */
#define MINPKTDATA	128		/* Minimum data to send */
#define MAXPKTSOUT	16		/* Maximum outstanding packets */
#define MAXBYTESOUT	8192		/* Maximum outstanding data bytes */
#define MAXTOTALPKTS	16		/* Maximum total packets out */
#define MAXTOTALBYTES	8192		/* Maximum total data bytes out */
#define WINDOW		6		/* Window (for acknowledging) */

/*
 * Tunable variables.  These depend on the amount
 * of memory which can be consumed by BULK, the
 * round-trip time to the remote machine and
 * other factors in the network.  These can affect
 * the total throughput of the protocol.
 */
int		bulk_alreadytuned;	/* Set if values have been tuned */
int		bulk_maxpktdata;	/* Maximum data bytes per packet */
int		bulk_minpktdata;	/* Minimum data to send */
int		bulk_maxpktsout;	/* Maximum outstanding packets */
int		bulk_maxbytesout;	/* Maximum outstanding data bytes */
int		bulk_maxtotalpkts;	/* Maximum total packets out */
int		bulk_maxtotalbytes;	/* Maximum total data bytes out */
int		bulk_window;		/* Maximum receive window */

struct ifnet	*ifbulk;		/* Interface to lower layer */
bulk_connection_t *bulk_freeConnections; /* Head of free connection list */
bulk_packet_t	*bulk_freePackets;	/* Head of free packet list */
bulk_id_t	bulk_id;		/* Next useable connection ID */
int		bulk_debug;		/* Enable debugging messages */
bulk_connection_t *bulk_hashtable[HASHSIZE]; /* Hashed list of connections */
int		bulk_totalpkts;		/* Total packets outstanding */
int		bulk_totalbytes;	/* Total bytes outstanding */

bulk_connection_t *bulk_allocConnection(), *bulk_findConnection();
bulk_packet_t *bulk_makePacket();
int bulk_daemon();

bulk_init()
{
	/*
	 * Hook up with the first driver that can carry BULK
	 * packets.  If we can't find one then return.
	 */
	ifbulk = (struct ifnet *)0;

	bulk_intrq.ifq_maxlen = IFQ_MAXLEN;

	/*
	 * Initialize tunable variables if they
	 * have not already been set.
	 */
	if (!bulk_alreadytuned)
	{
		bulk_maxpktdata = MAXPKTDATA;
		bulk_maxpktsout = MAXPKTSOUT;
		bulk_maxbytesout = MAXBYTESOUT;
		bulk_maxtotalpkts = MAXTOTALPKTS;
		bulk_maxtotalbytes = MAXTOTALBYTES;
		bulk_window = WINDOW;
	}
	timeout(bulk_daemon, 0, hz);
}

bulk_daemon()
{
	register bulk_connection_t **hashp;

	for (hashp = bulk_hashtable; hashp < &bulk_hashtable[HASHSIZE]; ++hashp)
	{
		register bulk_connection_t *bulkconn;

		for (bulkconn = *hashp; bulkconn; bulkconn = bulkconn->fwd)
		{
			register bulk_packet_t *packet;
			register int s = BULKSPL();

			for (packet = bulkconn->sendlist; packet; packet = packet->next)
			{
				if (packet->lastused < time.tv_sec + 2)
					bulk_sendout(bulkconn, packet);
			}
			splx(s);
		}
	}
	timeout(bulk_daemon, 0, hz);
}

/*
 * Create a new connection.  There is
 * currently no remote end of the connection
 * so we are the ones to allocate the
 * connection ID.  Return a pointer to
 * the connection descriptor or 0 if
 * the connection cannot be created.
 */
bulk_connection_t *
bulk_create(conn, clientInfo, clientArg)
   connection_t *conn;
   bulk_client_t *clientInfo;
   caddr_t clientArg;
{
	register bulk_connection_t *bulkconn;

	bulkconn = bulk_allocConnection(&myNode, bulk_id);
	bulkconn->connectionNode = myNode;
	bulkconn->connectionGen = myGeneration;
	bulkconn->connectionId = bulk_id++;
	bulkconn->conn = conn;
	bulkconn->firstflag = 0;
	bulkconn->clientInfo = clientInfo;
	bulkconn->clientArg = clientArg;
	bulkconn->connected = 0;
	bulkconn->sentreqack = 0;
	bulkconn->window = 2;
	BULK_INCSTAT(creates);
	return(bulkconn);
}

/*
 * Create a connection to a machine on which
 * the other end of the connection already
 * exists.  Return a pointer to the connection
 * descriptor or 0 if we fail.
 */
bulk_connection_t *
bulk_connect(conn, bulkid, clientInfo, clientArg, start)
   register connection_t *conn;
   bulk_id_t bulkid;
   bulk_client_t *clientInfo;
   caddr_t clientArg;
   int start;
{
	register bulk_connection_t *bulkconn;

	bulkconn = bulk_allocConnection(&conn->node, bulkid);
	bulkconn->connectionNode = conn->node;
	bulkconn->connectionGen = conn->generation;
	bulkconn->connectionId = bulkid;
	bulkconn->conn = conn;
	bulkconn->firstflag = BULKFLAG_FIRST;
	bulkconn->clientInfo = clientInfo;
	bulkconn->clientArg = clientArg;
	bulkconn->connected = 1;
	bulkconn->sentreqack = 0;
	bulkconn->window = 2;

	/*
	 * If we are to start data transfer on
	 * this connection then send an acknowledge
	 * to inform the other end that this one
	 * has been created.
	 */
	if (start)
	{
		bulk_sendack(bulkconn);
		BULK_INCSTAT(starts);
	}

	return(bulkconn);
}

/*
 * Destroy a connection.  This is called on
 * each end of a connection.
 */
bulk_destroy(bulkconn)
   register bulk_connection_t *bulkconn;
{
	register bulk_packet_t *packet;
	int s = BULKSPL();

	/*
	 * Free any packets which are still
	 * on the send list.
	 */
	for (packet = bulkconn->sendlist; packet; )
	{
		register bulk_packet_t *next = packet->next;

		--bulk_totalpkts;
		bulk_totalbytes -= packet->size;
		bulk_freePacket(packet);
		BULK_INCSTAT(flushed);
		packet = next;
	}
	bulk_freeConnection(bulkconn);
	splx(s);
}

/*
 * Request to send more data via BULK.
 * The caller would like to send size bytes
 * of data on bulkconn.  Return 0 if we
 * are able to (all previous data sent)
 * and EWOULDBLOCK if not.  After the
 * request is queued try to send the next
 * packet.
 */
int
bulk_send(bulkconn, size)
   register bulk_connection_t *bulkconn;
   int size;
{
	int s = BULKSPL();

	if (bulkconn->requestedleft != 0)
		return(EWOULDBLOCK);
	BULK_INCMSTAT(sends);
	bulkconn->requestedleft = size;
	while (bulk_sendnext(bulkconn))
		;
	splx(s);
	return(0);
}

/*
 * Send the next packet on a bulk data
 * connection if none of the limits have
 * been exceeded.  If we manage to send
 * a packet return 1, else return 0.
 */
int
bulk_sendnext(bulkconn)
   register bulk_connection_t *bulkconn;
{
	register bulk_packet_t *packet;
	register struct mbuf *m;
	register int size;

	if (bulkconn->requestedleft == 0 || !bulkconn->connected)
		return(0);

	/*
	 * If there are already too many packets or
	 * bytes outstanding then we cannot send another
	 * packet.
	 */
	if (bulkconn->pktsout >= bulk_maxpktsout ||
	    bulk_totalpkts >= bulk_maxtotalpkts ||
	    bulkconn->bytesout >= bulk_maxbytesout ||
	    bulk_totalbytes >= bulk_maxtotalbytes ||
	    bulkconn->pktsout >= bulkconn->window)
		return(0);

	/*
	 * We know that we can send a packet, so we
	 * must now determine the maximum size the
	 * packet can be.
	 */
	size = bulkconn->requestedleft;
	if (size > bulk_maxpktdata)
		size = bulk_maxpktdata;
	if (size > bulk_maxbytesout - bulkconn->bytesout)
		size = bulk_maxbytesout - bulkconn->bytesout;
	if (size > bulk_maxtotalbytes - bulk_totalbytes)
		size = bulk_maxtotalbytes - bulk_totalbytes;

	/*
	 * Get a packet descriptor for a packet
	 * of the size we have determined.  If
	 * we cannot get it then try half the
	 * size until a minimum is reached.
	 */
	for (;;)
	{
		packet = bulk_makePacket(bulkconn, size);
		if (packet)
			break;
		if (size <= bulk_minpktdata)
			return(0);
		size /= 2;
	}

	/*
	 * Now we have a packet descriptor and an
	 * mbuf chain for both the header and data.
	 * The packet descriptor has had most of
	 * its fields filled in and so has the header,
	 * so we now copy the data from the client
	 * into the mbuf
	 */
	bulkconn->requestedleft -= size;
	++bulk_totalpkts;
	bulk_totalbytes += size;
	++bulkconn->pktsout;
	bulkconn->bytesout += size;
	m = packet->m0;
	while (size > 0)
	{
		register int length;
		register int (*copyfunc)() = bulkconn->clientInfo->copy;
		register caddr_t arg = bulkconn->clientArg;

		while (m->m_len >= MLEN)
			m = m->m_next;
		length = MLEN - m->m_len;
		if (length > size)
			length = size;
		if (copyfunc)
			(*copyfunc)(arg, mtod(m, caddr_t)+m->m_len, length);
		m->m_len += length;
		size -= length;
	}

	/*
	 * If this is the last packet for a
	 * single bulk_send request then request
	 * an acknowledgement.
	 */
	if (bulkconn->requestedleft == 0)
	{
		(mtod(packet->m0, bulk_header_t *))->flags |= BULKFLAG_REQACK;
		++bulkconn->sentreqack;
		BULK_DECMSTAT(sends);
	}

	/*
	 * If we are half way to hitting a limit
	 * then send a request for acknowledgement.
	 */
	else if (bulkconn->sentreqack == 0 &&
		 (bulkconn->pktsout >= bulk_maxpktsout/2 ||
		  bulk_totalpkts >= bulk_maxtotalpkts/2 ||
		  bulkconn->bytesout >= bulk_maxbytesout/2 ||
		  bulk_totalbytes >= bulk_maxtotalbytes ||
		  bulkconn->pktsout >= bulkconn->window/2))
	{
		(mtod(packet->m0, bulk_header_t *))->flags |= BULKFLAG_REQACK;
		++bulkconn->sentreqack;
	}

	/*
	 * Add the packet to the list of packets
	 * for this connection and send the packet
	 * out.
	 */
	if (bulkconn->sendtail)
		bulkconn->sendtail->next = packet;
	else
		bulkconn->sendlist = packet;
	bulkconn->sendtail = packet;
	bulk_sendout(bulkconn, packet);
	return(1);
}

/*
 * A bulk data packet has been received from
 * the network.  Here we process it, possibly
 * starting output.  This function runs at
 * the interrupt priority of the underlying
 * network.
 */
bulk_input()
{
	for (;;)
	{
		register struct mbuf *m0;
		int s = BULKSPL();

		IF_DEQUEUE(&bulk_intrq, m0);
		splx(s);
		if (!m0)
			return;
		bulk_inputSingle(m0);
	}
}

bulk_inputSingle(m0)
   struct mbuf *m0;
{
	register bulk_header_t *header = mtod(m0, bulk_header_t *);
	register bulk_connection_t *bulkconn;
	int isduplicate = 0;

#ifdef DEBUG
	if (bulk_debug)
	{
		register connection_t *conn;

		if (conn = conn_findByAddress(&header->connectionNode))
		{
			bulk_showpkt(conn, header);
			conn_free(conn);
		}
	}
#endif DEBUG
	BULK_INCSTAT(inputs);

	/*
	 * Find the connection descriptor for
	 * the connection referred to in the header.
	 * If we cannot find it then free the packet
	 * and return.
	 */
	if ((bulkconn = bulk_findConnection(&header->connectionNode, header->connectionGen, header->connectionId, header->flags)) == 0)
	{
#ifdef DEBUG
		if (bulk_debug)
			printf(" < ???\n");
#endif DEBUG
		m_freem(m0);
		BULK_INCSTAT(dropped);
		return;
	}
	bulkconn->connected = 1;
	bulkconn->window = header->window;

#ifdef DEBUG
	if (bulk_debug)
		printf(" < %s\n", bulkconn->conn->name);
#endif DEBUG

	if (header->dataSeqno != bulk_nextSeqno(bulkconn->recvseqno))
	{
		register int hseqno = header->dataSeqno;
		register int rseqno = bulk_nextSeqno(bulkconn->recvseqno);

		/*
		 * If the packet is a duplicate then just
		 * throw it away.  We consider it a duplicate
		 * if the the sequence number is less than the
		 * expected sequence number, taking into
		 * account possible wrap-around of the
		 * sequence numbers.
		 */
		if (rseqno >= BULK_HALFSEQNO &&
		    rseqno-BULK_HALFSEQNO < hseqno && hseqno < rseqno ||
		    rseqno < BULK_HALFSEQNO &&
		    (hseqno < rseqno || rseqno+BULK_HALFSEQNO <= hseqno))
		{
			isduplicate = 1;
			BULK_INCSTAT(dups);
		}

		/*
		 * Since the packet does not have the
		 * expected sequence number, we will
		 * need to send an acknowledge and an
		 * indication that we missed a packet.
		 */
		else
		{
			bulkconn->reqack = 1;
			bulkconn->missed = 1;
			BULK_INCSTAT(missed);
		}
	}

	/*
	 * If this packet has the correct sequence
	 * number and contains data then we update
	 * the most recently received sequence number
	 * so that any acks generated will reflect
	 * this packet.
	 */
	else if (header->flags & BULKFLAG_DATA)
		bulkconn->recvseqno = header->dataSeqno;

	/*
	 * If the other end requests that we send
	 * an acknowledge then set a flag now so
	 * that any packets sent while we are processing
	 * this packet will contain the acknowledge.
	 */
	if (header->flags & BULKFLAG_REQACK)
		bulkconn->reqack = 1;

	/*
	 * If there was an acknowledge in the packet
	 * then we can update our records and throw
	 * away some packets that we are keeping.
	 */
	if (header->flags & BULKFLAG_ACK && !isduplicate)
	{
		register bulk_packet_t *packet = bulkconn->sendlist;

		BULK_INCSTAT(acks);
		/*
		 * Make sure that acknowledge is for packets
		 * that we still have in our send list.
		 */
		while (packet && packet->seqno != header->ackSeqno)
			packet = packet->next;

		/*
		 * If the acknowledge sequence number looks
		 * valid then throw away all packets up-to
		 * and including that one.
		 */
		if (packet)
		{
			register bulk_packet_t *nextPacket;

			packet = bulkconn->sendlist;
			for (;;)
			{
				register u_short seqno = packet->seqno;

				nextPacket = packet->next;
				--bulkconn->pktsout;
				bulkconn->bytesout -= packet->size;
				--bulk_totalpkts;
				bulk_totalbytes -= packet->size;
				if ((mtod(packet->m0, bulk_header_t *))->flags & BULKFLAG_REQACK)
					--bulkconn->sentreqack;
				bulk_freePacket(packet);
				if (seqno == header->ackSeqno)
					break;
				packet = nextPacket;
			}
			if (!(bulkconn->sendlist = nextPacket))
			{
				bulkconn->sendtail = 0;

				/*
				 * If all outstanding packets have
				 * been acknowledged, there is no more
				 * to send and there is a "sent" function
				 * then call it.
				 */
				/* BUG is it really safe to call sent here */
				if (bulkconn->requestedleft == 0 &&
				    bulkconn->clientInfo->sent)
					(*bulkconn->clientInfo->sent)(
					     bulkconn->clientArg);
				if (bulkconn->requestedleft == 0)
					BULK_INCSTAT(empty);
			}

			/*
			 * If the receiver missed a packet then
			 * we start retransmitting those packets
			 * after the last one it did receive.
			 */
			else if (header->flags & BULKFLAG_MISSED)
				bulk_resend(bulkconn, bulk_nextSeqno(header->ackSeqno));
		}
	}

	/*
	 * If there was data in the packet then
	 * give it to the client (only if there
	 * is a receive function).
	 */
	if ((header->flags & BULKFLAG_DATA) &&
	    header->dataSeqno == bulkconn->recvseqno &&
	    !isduplicate &&
	    bulkconn->clientInfo->recv)
	{
		register struct mbuf *m = dtom(header);
		register int (*recvfunc)() = bulkconn->clientInfo->recv;
		register caddr_t arg = bulkconn->clientArg;
		register int count = header->size;
		register int length = m->m_len - sizeof(*header);

		BULK_INCSTAT(recvData);
		BULK_ADDSTAT(recvBytes,count);
		if (count > 0 && length > 0)
		{
			if (length > count)
				length = count;
			(*recvfunc)(arg, mtod(m, caddr_t)+sizeof(*header),
				    length);
			count -= length;
		}
		while (count > 0 && (m = m->m_next))
		{
			if ((length = m->m_len) > count)
				length = count;
			(*recvfunc)(arg, mtod(m, caddr_t), length);
			count -= length;
		}
	}

	/*
	 * Start sending packets if possible.
	 */
	while (bulk_sendnext(bulkconn))
		;

	/*
	 * If an acknowledge was requested and no
	 * packets have been sent while we were
	 * processing this packet then send one.
	 */
	if (bulkconn->reqack)
		bulk_sendack(bulkconn);

	/*
	 * We are done so throw the packet
	 * away.
	 */
	m_freem(m0);
}

/*
 * Resend the packets still in the send
 * list.
 */
/*ARGSUSED*/
bulk_resend(bulkconn, seqno)
   register bulk_connection_t *bulkconn;
   int seqno;
{
	register bulk_packet_t *packet;

	for (packet = bulkconn->sendlist; packet; packet = packet->next)
	{
		BULK_INCSTAT(resends);
		bulk_sendout(bulkconn, packet);
	}
}

/*
 * Send an acknowledge on a connection.
 * Do not add this packet to the send list
 * since it does not contain any data (and,
 * therefore, does not consume a sequence
 * number.
 */
bulk_sendack(bulkconn)
   register bulk_connection_t *bulkconn;
{
	register bulk_packet_t *packet;

	if ((packet = bulk_makePacket(bulkconn, 0)) == 0)
		return;
	bulk_sendout(bulkconn, packet);
	bulk_freePacket(packet);
	BULK_INCSTAT(sendacks);
}

/*
 * Send a packet out over the network.
 * Modify the header, if necessary before
 * sending it.
 */
bulk_sendout(bulkconn, packet)
   register bulk_connection_t *bulkconn;
   register bulk_packet_t *packet;
{
	register bulk_header_t *header = mtod(packet->m0, bulk_header_t *);
	rpc_sockaddr_t sockaddr;

	/*
	 * Since we are sending a packet we also
	 * send an acknowledge.
	 */
	header->ackSeqno = bulkconn->recvseqno;
	header->flags |= BULKFLAG_ACK;
	bulkconn->reqack = 0;

	/*
	 * If we missed a packet then we need to
	 * send the missed bit indicating this
	 * fact to the sender so that it can re-
	 * send packets beginning with the one
	 * we missed.
	 */
	if (bulkconn->missed)
	{
		header->flags |= BULKFLAG_MISSED;
		bulkconn->missed = 0;
	}

#ifdef DEBUG
	if (bulk_debug)
	{
		register connection_t *conn;

		if (conn = conn_findByAddress(&header->connectionNode))
		{
			bulk_showpkt(conn, header);
			conn_free(conn);
		}
		printf(" > %s\n", bulkconn->conn->name);
	}
#endif DEBUG

	if (bulkconn->conn == NULL || bulkconn->conn->state != CONN_ACTIVE)
		return;
	sockaddr.addr_family = AF_BULK;
	sockaddr.node = bulkconn->conn->node;
	if (!ifbulk)
		return ENXIO;
	(*ifbulk->if_output)(ifbulk, m_copy(packet->m0, 0, M_COPYALL),
			    &sockaddr);
	packet->lastused = time.tv_sec;
	BULK_INCSTAT(outputs);
}

/*
 * Make a packet with enough mbufs to
 * hold size bytes of data (plus the
 * header).  Return a pointer to it
 * or 0 if we fail.
 */
bulk_packet_t *
bulk_makePacket(bulkconn, size)
   register bulk_connection_t *bulkconn;
   int size;
{
	register bulk_packet_t *packet;
	register bulk_header_t *header;
	register struct mbuf *m;

	if (packet = bulk_freePackets)
		bulk_freePackets = packet->next;
	else
		packet = (bulk_packet_t *)calloc(sizeof(bulk_packet_t),
					C_WAIT);

	packet->next = 0;
	packet->seqno = bulk_nextSeqno(bulkconn->sendseqno);
	packet->size = size;
	packet->m0 = getmbuf(sizeof(*header) + size);
	for (m = packet->m0; m; m = m->m_next)
		m->m_len = 0;
	packet->m0->m_len = sizeof(*header);

	/*
	 * If this packet will contain data then
	 * we consume the next sequence number.
	 */
	if (size != 0)
		bulkconn->sendseqno = packet->seqno;

	/*
	 * At this point we have all of the information
	 * necessary to fill in the packet header.  The
	 * only fields which will be modified later are
	 * the ack sequence number and possibly the flag
	 * field to set some bits.
	 */
	header = mtod(packet->m0, bulk_header_t *);
	header->version = BULK_VERSION;
	header->flags = size ? BULKFLAG_DATA : 0;
	if (!bulkconn->firstflag)
		header->flags |= BULKFLAG_FIRST;
	header->connectionNode = bulkconn->connectionNode;
	header->connectionGen = bulkconn->connectionGen;
	header->connectionId = bulkconn->connectionId;
	header->dataSeqno = packet->seqno;
	header->ackSeqno = 0;
	header->size = size;
	header->window = bulk_window;

	BULK_INCMSTAT(sendPackets);
	BULK_ADDMSTAT(sendBytes,packet->size);
	return(packet);
}

/*
 * Free a packet and all memory associated
 * with it.
 */
bulk_freePacket(packet)
   register bulk_packet_t *packet;
{
	BULK_SUBMSTAT(sendBytes,packet->size);
	BULK_DECMSTAT(sendPackets);
	m_freem(packet->m0);
	packet->next = bulk_freePackets;
	bulk_freePackets = packet;
}

/*
 * Allocate a connection descriptor from the
 * free list or from allocatable memory.
 * Fill in some of the fields in the
 * descriptor and return a pointer to it.
 */
bulk_connection_t *
bulk_allocConnection(nodep, connectionId)
   node_t *nodep;
   bulk_id_t connectionId;
{
	register bulk_connection_t *bulkconn;
	register bulk_connection_t **hashp;

	if (bulkconn = bulk_freeConnections)
		bulk_freeConnections = bulkconn->fwd;
	else
		bulkconn = (bulk_connection_t *)calloc(sizeof(*bulkconn));
	bulkconn->requestedleft = 0;
	bulkconn->sendseqno = 0;
	bulkconn->pktsout = 0;
	bulkconn->bytesout = 0;
	bulkconn->sendlist = 0;
	bulkconn->sendtail = 0;
	bulkconn->recvseqno = 0;

	hashp = &bulk_hashtable[(nodep->host.low ^ connectionId) % HASHSIZE];
	bulkconn->fwd = *hashp;
	*hashp = bulkconn;

	BULK_INCMSTAT(connections);
	return(bulkconn);
}

/*
 * Find the bulk connection record which
 * corresponds to the node, generation, and
 * id supplied.  Return a pointer to the
 * bulk connection record if found and 0
 * if not.
 */
bulk_connection_t *
bulk_findConnection(nodep, generation, id, firstflag)
   register node_t *nodep;
   register long generation;
   register bulk_id_t id;
   register u_short firstflag;
{
	register bulk_connection_t *bulkconn;

	firstflag &= BULKFLAG_FIRST;
	bulkconn = bulk_hashtable[(nodep->host.low ^ id) % HASHSIZE];
	while (bulkconn &&
	         (!nodecmp(bulkconn->connectionNode, *nodep) ||
	          bulkconn->connectionGen != generation ||
		  bulkconn->connectionId != id ||
		  bulkconn->firstflag != firstflag))
		bulkconn = bulkconn->fwd;
	return(bulkconn);
}

/*
 * Remove a connection from the hash
 * table and free the memory associated
 * with it.
 */
bulk_freeConnection(bulkconn)
   register bulk_connection_t *bulkconn;
{
	register bulk_connection_t **ptr;

	for (ptr = &bulk_hashtable[(bulkconn->connectionNode.host.low ^ bulkconn->connectionId) % HASHSIZE];
	     *ptr && *ptr != bulkconn; ptr = &(*ptr)->fwd)
		;
	if (*ptr != bulkconn)
		panic("bulk_freeConnection");
	*ptr = bulkconn->fwd;
	bulkconn->fwd = bulk_freeConnections;
	bulk_freeConnections = bulkconn;
	BULK_DECMSTAT(connections);
}

#ifdef DEBUG
bulk_showpkt(conn, header)
   connection_t *conn;
   bulk_header_t *header;
{
	printf("BULK: %s/%08x f=%c%c%c%c%c ds=%04x as=%04x s=%04x w=%04x",
		conn->name,
		header->connectionId,
		(header->flags & BULKFLAG_DATA ? 'D' : ' '),
		(header->flags & BULKFLAG_ACK ? 'A' : ' '),
		(header->flags & BULKFLAG_REQACK ? 'R' : ' '),
		(header->flags & BULKFLAG_MISSED ? 'M' : ' '),
		(header->flags & BULKFLAG_FIRST ? 'F' : ' '),
		header->dataSeqno, header->ackSeqno, header->size,
		header->window);
}
#endif DEBUG

#ifdef DEBUG

struct bulkinfo { int size; caddr_t addr; };

int bulk_nullcopy();
int bulk_nullsent();

bulk_client_t firstclient = { 0, bulk_nullcopy, bulk_nullsent, 0 };
bulk_client_t secondclient = { 0, 0, 0, 0 };
bulk_connection_t *firstconn;
bulk_connection_t *secondconn;
char bulkbuffer[512];
char *bulkcp;

bulk_ioctl(dev, cmd, data, flag)
   dev_t dev;
   int cmd;
   caddr_t data;
   int flag;
{
	register connection_t *conn;

	switch (cmd)
	{

	case BULK_IOCCREATE:
		conn = conn_findByAddress(&myNode);
		if ((firstconn = bulk_create(conn, &firstclient, 0)) == 0)
			return(EIO);
		if ((secondconn = bulk_connect(conn, bulk_getid(firstconn), &secondclient, 0, 0)) == 0)
		{
			bulk_destroy(firstconn);
			return(EIO);
		}
		break;

	case BULK_IOCDESTROY:
		bulk_destroy(secondconn);
		bulk_destroy(firstconn);
		bulkcp = 0;
		break;

	case BULK_IOCSEND:
		if (bulkcp != 0)
			return(EIO);
		bulkcp = bulkbuffer;
		copyin(((struct bulkinfo *)data)->addr, bulkbuffer,
			((struct bulkinfo *)data)->size);
		return(bulk_send(firstconn, ((struct bulkinfo *)data)->size));

	}
	return(0);
}

bulk_nullcopy(arg, buffer, count)
   int arg;
   caddr_t buffer;
   int count;
{
	bcopy(bulkcp, buffer, count);
	bulkcp += count;
}

bulk_nullsent(arg)
   int arg;
{
	bulkcp = 0;
}
#endif DEBUG
