/*
 * Read from an EFS file using the
 * bulk data transfer protocol.
 *
 * jam 850301
 * jht 850829 -- ifdef'd the bulk transfer facility with EFS_BULKDATA.
 */

#ifdef	EFS_BULKDATA
#include "../h/param.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/buf.h"
#include "../h/uio.h"
#include "../h/vmparam.h"
#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../efs/efs.h"
#include "../vnet/bulkdata.h"

extern bulk_connection_t *bulk_create();
extern bulk_connection_t *bulk_connect();

struct efs_bulkreadCall {
	u_long		rfd;		/* Remote file descriptor */
	long		offset;		/* Offset into remote file */
	int		size;		/* Number of bytes being read */
	bulk_id_t	bulkid;		/* ID of local BULK connection */
};

struct efs_bulkreadReturn {
	char		junk;
	char		interrupted;	/* Call was interrupted by signal */
	char		eosys;		/* How to return from system call */
	char		error;		/* Error number of 0 on success */
	int		size;		/* Number of bytes actually read */
};

int efs_bulkreadRecv();
int efs_bulkreadCopy();
int efs_bulkreadSent();

bulk_client_t efs_brcclient = { efs_bulkreadRecv, 0, 0, 0 };
bulk_client_t efs_brsclient = { 0, efs_bulkreadCopy, efs_bulkreadSent, 0 };

int
efs_bulkread(uio, remote, addr, size, offset, rsizep)
   struct uio *uio;
   register struct efs_remoteFile *remote;
   caddr_t addr;
   int size;
   int offset;
   int *rsizep;
{
	register bulk_connection_t *bulkconn;
	register struct efs_bulkreadCall *params;
	register struct efs_bulkreadReturn *results;
	int error;

	/*
	 * Lock the memory to which the transfer
	 * is going to take place.
	 */
	if (!useracc(addr, size, B_WRITE)) {
		*rsizep = 0;
		return(EFAULT);
	}
	vslock(addr, size);
	remote->procp = u.u_procp;
	remote->addr = addr;
	remote->resid = size;

	/*
	 * Make sure that the remote end has the
	 * correct environment for this transfer.
	 */
	efs_checkenv(remote);

	/*
	 * Create the receiving end of the
	 * connection.
	 */
	bulkconn = bulk_create(remote->conn, &efs_brcclient, (caddr_t)remote);

	/*
	 * Send the RPC request to the server
	 * which will connect the other end; the
	 * data will be transferred, and the RPC
	 * will return.
	 */
	params = efs_makePacket(bulkread,0);
	params->rfd = remote->rfd;
	params->offset = offset;
	params->size = size;
	params->bulkid = bulk_getid(bulkconn);
	results = (struct efs_bulkreadReturn *)
			efs_call(remote->conn,EFS_BULKREAD,params);

	/*
	 * Unlock the memory that we locked and
	 * destroy the connection.
	 */
	pcopy_unlock(u.u_procp);
	vsunlock(addr, size, B_READ);
	bulk_destroy(bulkconn);

	if (results == NULL) {
		*rsizep = 0;
		return(u.u_error);
	}
	if (remote->resid != size - results->size)
		panic("efs_bulkread");

	error = results->error;
	*rsizep = results->size;
	uioflush(results->size, uio);

	/*
	 * If the call was interrupted on the
	 * remote end then we perform a longjmp
	 * here, effecting the equivalent of a
	 * single longjmp from the sleep() function
	 * on the remote machine to the previous
	 * setjmp on this machine.
	 */
	if (results->interrupted) {
		u.u_error = error;
		u.u_eosys = results->eosys;
		rpc_freeResults(results);
		longjmp(&u.u_qsave);
	}
	rpc_freeResults(results);
	return(error);
}

caddr_t
efs_remoteBulkread(clientConn, clientId, operation, params)
   connection_t *clientConn;
   u_long clientId;
   u_short operation;
   struct efs_bulkreadCall *params;
{
	register struct efs_bulkreadReturn *results;
	register efs_localFile_t *local;

	results = efs_makeReturnPacket(params,bulkread,0);
	if ((local = efs_rfdToLocal(params->rfd)) == NULL) {
		results->error = EBADF;
		results->interrupted = 0;
		results->size = 0;
	} else {
		bulk_connection_t *bulkconn;
		struct uio uuio;
		struct iovec uiov;
		caddr_t addr;

		/*
		 * Grab the correct environment for this
		 * file descriptor.
		 */
		efs_takeenv(local);

		/*
		 * Allocate the memory from which the
		 * transfer is going to take place.
		 */
		prpc_memsize(params->size);
		addr = (caddr_t)USRTEXT;

		/*
		 * Read from the file into the memory
		 * we allocated.
		 */
		uiofilluser(&uuio, &uiov, params->offset, addr, params->size);
		EFS_SETJMP();
		u.u_error = rwip((struct inode *)local->fp->f_data, &uuio,
				 UIO_READ);
		EFS_UNSETJMP();
		results->size = params->size - uuio.uio_resid;

		if (results->size == 0)
			goto readzero;

		/*
		 * Create the sending end of the connection,
		 * lock the memory, and transfer the data.
		 */
		local->procp = u.u_procp;
		local->addr = addr;
		local->resid = results->size;
		bulkconn = bulk_connect(local->conn, params->bulkid,
					&efs_brsclient, (caddr_t)local, 0);
		vslock(addr, results->size);
		/* BUG should be at BULKSPL */
		bulk_send(bulkconn, results->size);
		while (local->resid != -1)
			/* BUG should sleep at PRIxxx */
			sleep((caddr_t)local, PZERO);

		/*
		 * For now we always destroy the bulk
		 * data connection.
		 */
		bulk_destroy(bulkconn);

		/*
		 * Unlock and free the memory.
		 */
		pcopy_unlock(u.u_procp);
		vsunlock(addr, results->size, B_WRITE);
	readzero:
		prpc_memsize(0);
	}
	rpc_freeParams(params);
	return((caddr_t)results);
}

efs_bulkreadRecv(remote, buffer, count)
   register struct efs_remoteFile *remote;
   caddr_t buffer;
   register int count;
{
	pcopy_lock(remote->procp);
	pcopyout(remote->procp, buffer, remote->addr, count);
	remote->addr += count;
	remote->resid -= count;
}

efs_bulkreadCopy(local, buffer, count)
   register efs_localFile_t *local;
   caddr_t buffer;
   register int count;
{
	pcopy_lock(local->procp);
	pcopyin(local->procp, local->addr, buffer, count);
	local->addr += count;
	local->resid -= count;
}

efs_bulkreadSent(local)
   register efs_localFile_t *local;
{
	if (local->resid != 0)
		panic("efs_bulkreadSent");
	local->resid = -1;
	wakeup((caddr_t)local);
}
#endif	EFS_BULKDATA
