/*static char *SCCSID = "@(#)vlwrite.c	6.6 92/01/31";*/
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include "const.h"
#include "chain.h"
#include "misc.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "storage.h"
#include "tables.h"
#include "lw.h"
#include "frame.h"
#include "mmphiseg.h"
#define	 INCL_DOSSEGINFO
#include "fsdata.h"
#include "pbmacros.h"
#include "log.h"
#include "strat2.h"
#include "asyncio.h"
#include "pr_CACHE.h"
#include "pr_LAZY.h"
#include "pr_MISC.h"
#include "pr_DSKIO.h"
#if defined(DEBUG) || defined(DEBUG_LW)
    #include "pr_DEBUG.h"
#endif
//#include "mmph.h"

/***	VLWrite - write out a block of VDU buffers
 *
 *	VLWrite is called to flush at least one buffer.  We look in
 *	the cache to see if there are other buffers contiguous with
 *	this one that are also dirty.  We start at the requested buffer
 *	and look backwards (in sector order) to find buffers that are
 *	eligible for flushing (dirty).  Once we have the starting point,
 *	we move forward, copying buffers from there into our temp buffer
 *	and marking them clean.  We continue to do so until we exceed the
 *	capacity of the buffer.  Note that we always guarantee that the
 *	passed-in buffer gets written.
 *
 *	Otherwise, we will batch up to a max of XXX buffers.  We copy them
 *	into a buffer supplied by the lazy guy, mark the source buffers
 *	clean, and then queue the I/O from our buffer.
 *
 *	We must worry about two problems.  The first is that of
 *	a buffer that we mark as being clean here actually becoming
 *	dirty again and being written out.  Given that we have no control over
 *	either the head position or the sorting algorithm of the device driver
 *	we must make sure that the buffers being written out are locked to
 *	prevent other write activities.  Note that we must mark them clean
 *	as blocking here would give other threads the opportunity to dirty
 *	them.
 *
 *	The other circumstance is recycling the buffer and then filling it
 *	with the same sector.  We can get out-of-sequence problems in this
 *	case with the buffer containing data that is older than what is
 *	being written out.  This is solved by making GFB ignore any buffer
 *	that is on the dirty list.
 *
 *
 *	ENTRY	(eax) = buffer that's eligible for writing
 *	EXIT	none
 *	USES	all
 *
 *	HISTORY
 *
 */
#if SPB == 4
/* clean maps dirty bits into number of leading clean buffers */

STATIC BYTE VDUclean [] = {
	4,  0,	1,  0,		/* 0000 0001 0010 0011 */
	2,  0,	1,  0,		/* 0100 0101 0110 0111 */
	3,  0,	1,  0,		/* 1000 1001 1010 1011 */
	2,  0,	1,  0		/* 1100 1101 1110 1111 */
};


/* dirty maps dirty bits into number of leading buffers needed to *
 * cover all dirty buffers					  */

STATIC BYTE VDUdirty [] = {
	0,  1,	2,  2,		/* 0000 0001 0010 0011 */
	3,  3,	3,  3,		/* 0100 0101 0110 0111 */
	4,  4,	4,  4,		/* 1000 1001 1010 1011 */
	4,  4,	4,  4		/* 1100 1101 1110 1111 */
};
#endif

#ifdef DEBUG_LW

PBUFNODE    pbufLwHold = NULL;

#endif

//#114986 changed to far call
void far
VLWrite (PBUFNODE pbuf, USHORT usTypeLazyIO, PFRAME pfrm)
{
    USHORT cbuf, csec;
    ULONG lsn;
    USHORT us;
    PBYTE pData;
    ULONG msSinceBoot;

#define LW_OldBufs		1		 /* write out old (over-age) buffers */

#ifdef DEBUG_LW
    dprintf ("VLWrite: starting writes, cbufDirtyVDU=%d\n", cbufDirtyVDU);

    pbufLwHold = pbuf;
#endif

#ifdef DEBUG
    if (pbuf->lsnSec == LSN_BOGUS) {
	dprintf (__FILE__": pbuf->lsnSec == LSN_BOGUS; pbuf=%x\n", pbuf);
	Int3 ();
    }
#endif

    /* Walk backwards to find beginning of contiguous, dirty range.
     *
     * NOTICE that we don't accept dirty buffers that are not on the dirty
     *	      chain yet.  There are cases where dirty bits are set but the
     *	      buffer is not added to the dirty chain because we know the buffer
     *	      will get changed again soon.  (See LDECreate in LDE.c)
     */

    /* get the current time */
    msSinceBoot = msNow ();

    cbuf = 0;
    lsn = pbuf->lsnSec;

    while ((cbuf < LWBUFCT) &&
	   ((pbuf = HashFind (lsn)) != NULL) &&
	   (pbuf->dcbpDirt.pbufFwd != NULL) &&
	   (pbuf->bDirtMask != 0))
     {
	if( lsn == 0 )
	{
	    lsn -= SPB;
	    break;
	}
	lsn -= SPB;
	cbuf++;
     }

#ifdef DEBUG
    if (cbuf == 0) {
	dprintf ("VLWrite: check 1, cBuf is ZERO!\n");
    }
#endif

    /* sector of last found dirty buf */
    lsn += SPB;

    /* Walk forward gathering the headers of all contiguous dirty buffers */
    cbuf = 0;

   while ((cbuf < LWBUFCT) &&
	   ((pbuf = HashFind (lsn)) != NULL) &&
	   (pbuf->dcbpDirt.pbufFwd != NULL) &&
	   pbuf->bDirtMask != 0)
     {
	   lsn += SPB;
	   VLWBufPtr [cbuf++] = pbuf;
     }

#ifdef DEBUG
    if (cbuf == 0) {
	dprintf ("VLWrite: check 2, cBuf is ZERO!\n");
    }
#endif

    /* copy them (cbuf = # of buffers to write) */

     pData = VLWBuf;
    /*
     * Copy the cache buffers in a loop
     */
    for (us = 0; us < cbuf; us++) {

     pbuf = VLWBufPtr [us];

//#114986
     if (pbuf->fFlag & BFL_LZREAD) {
       if (LazyRead (pbuf,pfrm)) {
      /*
       *  Got hard error reading the VDU block of data. This should never
       *  never happen with VDU I/O.
       */
         _asm int 3;
         return;   /* error occurred; probably a device error. Bail out */
       }
     }

     if (us == 0) {

#if SPB == 4
      lsn = pbuf->lsnSec + VDUclean [pbuf->bDirtMask];
      csec = SPB - VDUclean [pbuf->bDirtMask];
      pData = BYTE_COPY (((PBYTE) pbuf->pblkData) +
		 			(VDUclean [pbuf->bDirtMask] << SECSHIFT),
		        pData,
		        csec << SECSHIFT);
#else
      {
	  register USHORT usCleanRun = 0;
	  register USHORT usMask = 1;
     USHORT usDirtMask = pbuf->bDirtMask = SPBBITS;

	  for ( ; usCleanRun < SPB ; usCleanRun++, usMask <<= 1)
	     if (usMask & usDirtMask)
		   break;

	   lsn = pbuf->lsnSec + usCleanRun;
	   csec = SPB - usCleanRun;

    /* copy data block from buffer into lw buffer */
	  pData = BYTE_COPY (((PBYTE) pbuf->pblkData) + (usCleanRun << SECSHIFT),
		       pData,
		       csec << SECSHIFT);
     }
#endif

     }  /* end if (us == 0) */

     else if (us != cbuf-1) {
	 /* copy data block from buffer into lw buffer */
	   pData = BYTE_COPY (pbuf->pblkData, pData, SPB << SECSHIFT);
	   csec += SPB;
     }
     else { /* us == cbuf-1 */
    /* now do only what we have to from the last */
#ifdef DEBUG
    if (pbuf->bDirtMask > SPBBITS) {
	dprintf ("VLWrite: pbuf->bDirtMask=0x%x\n", pbuf->bDirtMask);
	Int3 ();
    }
#endif
#if SPB == 4
    csec += VDUdirty [pbuf->bDirtMask];
    pData = BYTE_COPY (pbuf->pblkData,
		       pData,
		       VDUdirty [pbuf->bDirtMask] << SECSHIFT);
#else
    {
	register USHORT usDirtyRun = SPB;
	register USHORT usMask = SPBBITS ^ (SPBBITS / 2);
	USHORT usDirtMask = pbuf->bDirtMask = SPBBITS;

	for ( ; usDirtyRun > 0 ; usDirtyRun--, usMask >>= 1)
	    if (usMask & usDirtMask)
		break;

	csec += usDirtyRun;
	pData = BYTE_COPY (pbuf->pblkData, pData, usDirtyRun << SECSHIFT);
    }
#endif

     } /* end if (us == cbuf-1) */

     DBCREM (pbuf);		/* remove buffer from dirty chain */
     pbuf->bDirtMask = 0;	/* mark buffer clean */
     pbuf->flDMask = 0;  /* file dirt mask = NULL file ID */

     cbufDirtyVDU--;

    /* if buffer not on LRU list, place it there */
     if (pbuf->dcbpLru.pbufFwd == pbuf) {
       if ((msSinceBoot - pbuf->msLastDirtied) >=
           (4*LWData.msIdleBuf) )
         PlaceBuf (pbuf,BP_TOSS);
        else
         PlaceBuf (pbuf, BP_KEEP);
     }

    } /* end for loop */

		/*  If necessary, Wakeup on pNoBufWaitChain */
     if (pNoBufWaitChain != 0) {
		    WakeupMisc2 (&(pNoBufWaitChain), LOB_NOBUF);
	  }

    /*
     * VLWBuf is now set up with the minimal number of sectors needed to
     * write out.  All buffers have been cleaned, removed from dirty chain,
     * held, and marked lazy-in-progress.
     *
     * Write out the data
     */

    /*
     * use dovolio rather than writesectors since our writesectors needs a
     * buf rather than a pbyte
     */

   /*
    * Set VDU DoVolIO busy flag
    */
    fVDULWIO = TRUE;

//    if( (us = DoVolIO (DVIO_OPWRITE,
    if( (us = DoVolIO (DVIO_OPWRITE | DVIO_OPRESMEM,
		       lsn,
		       &csec,
		       VLWBuf,pfrm)) != 0 ) {
	/*
	 *  Got hard error writing the block of data. This should never
	 *  never happen with VDU I/O.
	 */
//test
   _asm int 3;
    }

   /*
    * Reset VDU DoVolIO busy flag
    */
    fVDULWIO = FALSE;

#ifdef DEBUG_LW
    dprintf ("VLWrite: done with writes, cbufDirtyVDU=%d\n", cbufDirtyVDU);
#endif

}
