/********************************************************************
 ** NETWORK FILE SYSTEM SERVICE
 **
 ** (c) Spak, Darrell Tam, c9107253@ee.newcastle.edu.au (1994)
 ** phone (Australia) 049-829-710
 **                    49-829-710
 ** (c) SST Jan-Feb-Mar 1994
 **
 ** "READ" file support
 **
 ** TABS to 4
 ********************************************************************/

#include "/snd/everything.h"
#include <st/st_proto.h>

#include "nfsinternal.h"
#include "nfsglobs_protos.h"

/** INTERNAL READ-DOS-MSG Passed around */
struct IFHReadMsg {
	struct	IFHDosMsg	dm;
	struct	NFSStdReturn readret;			/* net-send this home */

	struct NFSRead	*readreq;				/* read-request */

	long			allocsize;				/* total memory alloc'd */
	long			expected_len;			/* expected read size */
	char			*buf[2];				/* 2 buffers */

	char			dos_inuse;				/* dos packet in use */
	char			net_inuse;				/* packet is out on the network */
	char			thisbuf;
	char			pad[1];


};
#define IFH_READMSG_SIZE(a) (sizeof(struct IFHReadMsg) + a)


/********************************************************************/
	static void free_retret_read(struct PktMsgRecv *pmr)
/********************************************************************/
{
struct IFHReadMsg	*ifhrm = (void *)pmr->user.user2;

#ifdef DEBUG
	fpf(out, "nfs_read: Freeing memory\n");
#endif

	FreeMem(ifhrm, ifhrm->allocsize);
	FREE_PKTMSGRECV(pmr);
}




/********************************************************************/
	static void handle_done_read(struct PktMsgRecv *pmr)
/********************************************************************/
{
struct IFHReadMsg	*ifhrm = (void *)pmr->user.user2;

/** NET-REPLY */
	pmr->user.user1 = free_retret_read;
	ifhrm->readret.remote_res2 = IFHMSG_RES2(&ifhrm->dm);

	INTERNAL_PREP_PKTMSG_DMA(&pmr->pm,
						EMEM_RETURNMSG, SERV_RETURN,
						&ifhrm->readret,
						ifhrm->readreq->sr.return_len);
	PUT_STDMSG(&pmr->pm.smsg, pmr->dmaport, TYPE_INTERNAL_SEND);
}



/********************************************************************/
static void
	handle_read(struct IFHReadMsg *ifhrm, struct PktMsgRecv *pmr)
/********************************************************************
       Only call this if !ifhrm->net_inuse && !ifhrm->dos_inuse
 ********************************************************************/
{
struct	NFSRead		*req = ifhrm->readreq;
struct	InternalFH	*ifh = ifhrm->dm.ifh;
long	bytes_read	= IFHMSG_RES1(&ifhrm->dm);

	ifhrm->net_inuse = 1;

/** DID WE READ ANY BYTES?? */
	if(bytes_read > 0) {
		ifhrm->readret.remote_res1 += bytes_read;			/* bytes read */
		req->sr.result_len -= bytes_read;					/* bytes to go */
		INTERNAL_PREP_PKTMSG_DMA(&pmr->pm, EMEM_RETURNMSG, SERV_RESULT,
					ifhrm->buf[ifhrm->thisbuf], bytes_read);

	/** MORE TO GO && READ OK?? SEND OUT ANOTHER DOS */
		if(req->sr.result_len > 0 && bytes_read == ifhrm->expected_len) {

			ifhrm->thisbuf ^= 1;							/* swap buffers */
			ifhrm->dos_inuse = 1;							/* dos msg in use now */

			if(ifhrm->expected_len > req->sr.result_len)
						ifhrm->expected_len = req->sr.result_len;

			/*INIT_ARGS_IFHDOSMSG(&ifhrm->dm,*/
			STD_PKT_ARGS3(&ifhrm->dm.dosmsg.sp, dosport,
					ACTION_READ,
					ifh->realfh.fh_Arg1,
					ifhrm->buf[ifhrm->thisbuf],
					ifhrm->expected_len);

			PutMsg(ifh->realfh.fh_Type, ifhrm);
		}

	/** NO MORE TO GO, SEND BACK "RETURN" AFTER THIS */
		else pmr->user.user1 = handle_done_read;

	/** SEND OFF THIS PACKET */
		PUT_STDMSG(&pmr->pm.smsg, pmr->dmaport, TYPE_INTERNAL_SEND);
		return;
	}

/** AN ERROR, SEND BACK THE "RETURN" MESSAGE */
	if(!ifhrm->readret.remote_res1) ifhrm->readret.remote_res1 = bytes_read;
	handle_done_read(pmr);
}


/********************************************************************/
	static void handle_read_net(struct PktMsgRecv *pmr)
/********************************************************************/
{
struct IFHReadMsg	*ifhrm = (void *)pmr->user.user2;
	ifhrm->net_inuse = 0;
	if(!ifhrm->dos_inuse) handle_read(ifhrm, pmr);
}


/********************************************************************/
	static void handle_read_dos(struct IFHReadMsg *ifhrm)
/********************************************************************/
{
struct PktMsgRecv *pmr = ifhrm->dm.pktmsg;
	ifhrm->dos_inuse = 0;
	if(!ifhrm->net_inuse) handle_read(ifhrm, pmr);
}



/********************************************************************/
	void *srv_read(struct PktMsgRecv *pmr)
/********************************************************************
        Reading is performed in a double buffered manner
 ********************************************************************/
{
struct NFSRead		*req = (void *)pmr->req_body;
struct InternalFH	*ifh =
		GET_INTERNALFH(req->remote_ifh, req->remote_majic);
struct IFHReadMsg	*ifhrm;
long   bufsize1, bufsize2;
long   allocsize;

	if(!ifh) return(&nfs_res_notfound);			/* filehandle doesn't exist! */

/** ALLOC BUFFERS & DOSMSG (ifhrm) */
	if((bufsize1 = MIN(rwmaxbuf, req->sr.result_len)) <= 0)
			return(&nfs_res_ok);
	if((bufsize2 = req->sr.result_len-bufsize1) < 0) bufsize2 = 0;
	bufsize2 = MIN(rwmaxbuf, bufsize2);

	ACT_ALLOCMSG_THINGY(ifhrm, allocsize = IFH_READMSG_SIZE(bufsize1 + bufsize2));

/** INIT EVERYTHING AND THE SEND THE DOS-MSG OFF */
	pmr->user.user1 = handle_read_net;			/* up above */
	pmr->user.user2 = (long)ifhrm;

	ifhrm->readret.remote_res1 = 0;				/* 0 bytes read at this moment */
	ifhrm->readret.remote_res2 = 0;				/* nothing */
	ifhrm->readreq = req;
	ifhrm->allocsize = allocsize;
	ifhrm->buf[1] = (ifhrm->buf[0] = (void *)&ifhrm[1]) + bufsize1;
	ifhrm->dos_inuse = 1;						/* dos packet out */
	ifhrm->net_inuse = 0;						/* net work pkt available */

	INIT_ARGS_IFHDOSMSG(&ifhrm->dm,
			ACTION_READ,						/* ACTION */
			ifh->realfh.fh_Arg1,				/* Arg1 file handle Arg1 */
			ifhrm->buf[ifhrm->thisbuf = 0],		/* Arg2 buffer */
			ifhrm->expected_len = bufsize1);	/* Arg3 length */

	ACT_PUT_IFHDOSMSG(ifhrm, dosport, handle_read_dos, ifh, pmr);
	return(NULL);
}
