/*static char *SCCSID = "@(#)qwrite.c	6.8 92/01/31";*/
#define INCL_DOSERRORS
#include <os2def.h>
#include <bse.h>
#include <fsh.h>
#include <..\..\dos\dosinc\osconst.h>
#include <strat2.h>
#include "const.h"
#include "misc.h"
#include "chain.h"
#include "buf.h"
#include "dir.h"
#include "volume.h"
#include "frame.h"
#include "storage.h"
#include "tables.h"
#include "asyncio.h"
#include "assert.h"
#include "lw.h"
#include "fsdata.h"
#include "pbmacros.h"
#include "pr_dskio.h"
#include "pr_misc.h"
#include "pr_read.h"
#include "pr_write.h"
#include "superb.h"
#include "pbmsg.h"
#include "shutdown.h"

#ifdef DEBUG
#include "pr_debug.h"
#endif

/**	QWrite - Queue Write Request
  *
  *	QWrite determines if asynchronous I/O support is available.  If not a
  *	call to DoVolIO is done.
  *
  *	Otherwise...
  *	QWrite queues a write request for the device driver.
  *	When the driver completes it will use the pNoteRec info
  *	to notify anyone waiting for the I/O to complete.
  *
  *	QWrite discards the request block when the request is done, unless an
  *	error occurs.  PIOHs with write error are chained together for later
  *	HotFixing.
  *
  *	QWrite writes an extent from a contiguous memory area, never from
  *	cache buffers.
  *
  *	ENTRY	vlsnStart = physical sector # at true start of subrun
  *		cSects = count of sectors to write
  *		pbData = transfer address
  *		pNoteRec = pointer to Notification Record
  *
  *	EXIT	returns 0 if successful; otherwise error code
  *
  *	HISTORY
  *	    07-Feb-90 by JR (John Rogers, JohnRo@Microsoft)
  *		- Changed to reflect new field names in IOH.
  *		- Added assertion checking of IOH, NOTEREC.
**/

#ifdef DEBUG
    USHORT usQWritesActive = 0;		/* Incr here, decr in IO_NOTIFY(). */
#endif

UINT
QWrite (LSN	 vlsnStart,	  /* sector number */
	USHORT	 cSects,	  /* number of sectors to write */
	PBYTE	 pbData,	  /* pointer to caller's buffer */
	PNOTEREC pNoteRec,	  /* pointer to current Notification Record */
	PBYTE	 pbEventFlags,	  /* pointer to event flags */
	PFRAME	 pfrm)
{
    UINT RetCode = NO_ERROR;
    USHORT iVol = IVOLFROMLSN (vlsnStart);
    PVOLTAB pVol = VolPtr [iVol];

    ASSERT_NOTEREC(pNoteRec);

	 RetCode = ProbeAddress(PB_OPREAD,pbData,cSects * SECSIZE,pfrm);
				
    if (RetCode)
      return(RetCode);

//test. force strat1
//  goto strat1;

    if (!IsAsyncIOable (vlsnStart)) {

//test
strat1:

	    RetCode = (UINT)DoVolIO (DVIO_OPWRITE, vlsnStart,
				     &cSects, pbData, pfrm);

    } else {

	if (pNoteRec->nxtbit > MAXNOTEBIT) {
	    /* too many requests pending; wait for pending requests to finish
	     * and clean up pioh chain in pNoteRec
	     */
	    RNM (pNoteRec, TRUE, pfrm);
	}

	if (pNoteRec->errmsk) {
	    /* some previous request reported error */
	    RetCode = ERROR_GEN_FAILURE;

	} else {
	    PIOH pioh;
	    UINT RetBits;
       USHORT SgEntries = ((cSects * SECSIZE) / PAGESIZE) + 2 + 1;

	    if ((pioh = GetIOHand(vlsnStart & ~SECMASK,1,SgEntries,pfrm))
	       == NULL) {
	/* no heap space for I/O handle */
	   RetCode = ERROR_NOT_ENOUGH_MEMORY;
	    } else {
		ASSERT_IOH(pioh);

       _disable ();                      //#86017

		/* set bit in NoteRec for this request */
		 pNoteRec->mask |= pNoteRec->nxtbit;

		/* Set semaphore for this request.  We will block (in RNM)
		 * when there are 4 requests queued and any are not complete
		 * or when the write is done.  Since IO_Notify runs at interupt
		 * time, it will always clear the semaphore.  That way RNM can
		 * do a SemRequest and doesn't have to worry if the semaphore
		 * was just cleared.
		 */

		MAYBLOCK (FSH_SEMSET (&(pNoteRec->block)));

		/* Attach pNoteRec & pNoteRec->nxtbit onto *pioh */
		pioh->pNoteRec = pNoteRec;
		pioh->bBitForThisOp = (BYTE) pNoteRec->nxtbit;
		pioh->pVirtData = (PVOID) pbData;

		/* Shift pNoteRec->nxtbit left 1 */
		pNoteRec->nxtbit <<= 1;

      _enable ();                    //#86017

#ifdef DEBUG_ASYNC
		dprintf (__FILE__": Calling QDiskOp; lsn = %lx, cSects = %d, pbData = %p\n",
			 vlsnStart, cSects, pbData);
#endif

#ifndef MINIFSD
	/*
	 * If this is first write to volume, mark the volume as dirty by
	 * setting the volume dirty flag in the spares block.
	 * Note: WriteSparesFlag will update pVol->bSparesFlags.
	 */

	if ( ( ! (pVol->bStatusFlags & VS_MOUNTING) ) &&     /* not forced, */
	     ( ! (pVol->bSpareFlags & SPF_DIRT) ) &&	/* not yet dirty, */
	     ( ! fShuttingDown ) ) {		/* and not shutting down, */
	    PVOLTAB pvolOld = pfrm->pvol;	/* then save our volume ptr. */

	    pfrm->pvol = pVol;		/* set pvol for WriteSparesFlag, */
	    WriteSparesFlag(SPF_DIRT,pfrm);  /* and mark volume as dirty. */
	}
#endif /* !MINIFSD */

      pfrm->pvol = pVol;		/* set pvol for QDiskOp */

		if ((RetBits = QDiskOp (pioh,
					PRIO_BACKGROUND_USER, PB_WRITE_X,
					vlsnStart, cSects, pbData,
					pbEventFlags, pfrm)) != 0) {
#ifdef DEBUG
		    dprintf ("%s (%d): QDiskOp failed; %x\n",
			     __FILE__, __LINE__, RetBits);
		    Int3 ();
#endif
		    RetCode = ERROR_PROTECTION_VIOLATION;
		} else {

       msLastIO = msNow();  /* update system's time of most recent IO */

#ifdef DEBUG
		    ++usQWritesActive;
#endif

      	 *pbEventFlags |= IO_DONE_EVENT;  /* flag = IO_DONE */

		    SetListNotify (pioh, IONFY_COMPLETE, IO_Notify);

		    if ((RetBits = ExecIOH (pioh, FALSE, pfrm)) != 0) {
#ifdef DEBUG
			dprintf ("%s (%d): ExecIOH failed; %x\n",
				 __FILE__, __LINE__, RetBits);
			Int3 ();
#endif
#ifdef DEBUG
			--usQWritesActive;
#endif
			RetCode = ERROR_WRITE_FAULT;
		    }
		}
	    }
	}
    }

    return RetCode;
}   /* QWrite */
