/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "%w% %e%";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = FLP1OSM2.C
 *
 * DESCRIPTIVE NAME = Outer State Machine - Module 2
 *
 *
 *
 * VERSION = V2.1
 *
 * DATE = 94/03/14
 *
 * DESCRIPTION :
 *
 * Purpose:  Functions are implemented this OSM Module:
 *
 *               FLP1OSM2.C - IORB processing for
 *                              - IOCC_EXECUTEIO
 *                              - IOCC_FORMAT
 *                            Determine Sector Size Code (N)
 *                            Determine if DMA Buffer required
 *
 *
 *
*/
#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"
#include "dos.h"

#include "dskinit.h"

#include "iorb.h"
#include "addcalls.h"
#include "dhcalls.h"

#define INCL_INITRP_ONLY
#include "reqpkt.h"

#include "flp1cons.h"
#include "flp1regs.h"
#include "flp1misc.h"
#include "flp1type.h"
#include "flp1extn.h"
#include "flp1pro.h"
#include "flp1mif.h"


/*------------------------------------------------------------------------*/
/*  IOCC_EXECUTEIO                                                        */
/*  --------------                                                        */
/*------------------------------------------------------------------------*/

/*-------------------------------*/
/*                               */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID NEAR ExecuteIO( NPACB npACB )
{
  PADDWSP       pWSP;

  pWSP = (PADDWSP) npACB->pIORB->ADDWorkSpace;

  /*------------------------------------------------------------*/
  /* ExecuteIO requires we know the type of media in the drive  */
  /*                                                            */
  /* If the type of media in the drive is unknown, then execute */
  /* the media determination sequence, and then restart this    */
  /* IORB.                                                      */
  /*------------------------------------------------------------*/
  if ( !(npACB->npUCB->Flags & UCBF_MEDIA_UNCERTAIN) )
  {
    /*---------------------------------------------------------*/
    /* The FAST path option is used for chained IORB requests  */
    /* where we have determined that the IORBs are closely     */
    /* related enough that we can do a partial initialization  */
    /* between requests.                                       */
    /*---------------------------------------------------------*/
    if ( pWSP->Flags == WSF_FAST_PATH )
    {
      ExecIOPartial( npACB );
    }
    else
    {
      ExecIOInit(  npACB );

      if ( !(npACB->Flags & ACBF_OSM_WAITSTATE) )
      {
        ExecIOStart( npACB );
      }
    }
  }
  else
  {
    npACB->OState = ACBO_DETERMINE_MEDIA;
  }
}

/*-------------------------------*/
/*                               */
/*  ExecIOInit                   */
/*  ----------                   */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID NEAR ExecIOInit( NPACB npACB )
{
  PIORB_EXECUTEIO       pIORB;
  PADDWSP               pWSP;

  (PIORB) pIORB = npACB->pIORB;

  pWSP = (PADDWSP) npACB->pIORB->ADDWorkSpace;

  npACB->OpFlags = pWSP->Flags;
  npACB->OpCode  = ((PIORB) pIORB)->CommandModifier;

  if ( npACB->OpCode == IOCM_WRITE_VERIFY )
  {
    npACB->OpCode   = IOCM_WRITE;
    npACB->OpFlags |= OPF_VERIFY_REQD;
  }

  /*---------------------------------------------------------*/
  /* If this IORB requires a DMABuffer and we currently      */
  /* do not have a buffer allocated, Start a context thread  */
  /* to allocate the buffer. Once the buffer is allocated    */
  /* the IORB will be restarted.                             */
  /*---------------------------------------------------------*/
  if ( (npACB->OpFlags & OPF_DMA_BUF_REQD) && !npACB->DMABuf )
  {
    npACB->Flags  |= ACBF_OSM_WAITSTATE;

    StartBufferAlloc( npACB );

    goto InitIOFullExit;
  }

  /*------------------------------------------*/
  /* Setup sector counters for this operation */
  /*------------------------------------------*/
  npACB->SecTotal   = pIORB->BlockCount;
  npACB->SecCount   = pIORB->BlockCount;
  npACB->SecRemain  = pIORB->BlockCount;

  npACB->ExecIOError = 0;
  npACB->ExecIORecal = 0;

  npACB->RBA = pIORB->RBA;

  if (pIORB->iorbh.RequestControl & IORB_CHS_ADDRESSING )
  {
    npACB->OpFlags |= OPF_CHS_ADDRESSING;
  }

  /*-------------------------------------------*/
  /* Convert RBA to CHS addressing if required */
  /*-------------------------------------------*/
  if (npACB->OpFlags & OPF_CHS_ADDRESSING )
  {
    npACB->CHSAddr = *(PCHS_ADDR) &npACB->RBA;
  }
  else
  {
    f_ADD_ConvRBAtoCHS(npACB->RBA, &npACB->npMIF->Geo, &npACB->CHSAddr );
  }

  InitIOFullExit: ;
}

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  ExecIOStart                                                       */
/*  -----------                                                       */
/*                                                                    */
/* Note: This code is shared between IOCC_EXECUTEIO and IOCC_FORMAT.  */
/*                                                                    */
/*       Since these IORBs only match upto the SGList fields,         */
/*       anyother information is obtained from the ACB.               */
/*       This information is setup up by InitExecIOFull or            */
/*       InitFormatVerify prior to calling this routine.              */
/*                                                                    */
/*--------------------------------------------------------------------*/

VOID NEAR ExecIOStart( NPACB npACB )
{
  PADDWSP               pWSP;
  PIORB_EXECUTEIO       pIORB;


  pIORB = (PIORB_EXECUTEIO) npACB->pIORB;
  pWSP  = (PADDWSP) pIORB->iorbh.ADDWorkSpace;

  npACB->ReqCmd[1]  = npACB->UnitId | ((npACB->CHSAddr.Head & 1) << 2);
  npACB->ReqCmd[2]  = npACB->CHSAddr.Cylinder;
  npACB->ReqCmd[3]  = npACB->CHSAddr.Head;
  npACB->ReqCmd[4]  = npACB->CHSAddr.Sector;

  npACB->ReqCyl     = npACB->ReqCmd[2];

  /*----------------------------------------------*/
  /* Setup miscellaneous controller command bytes */
  /*----------------------------------------------*/
  npACB->ReqCmd[5]  = pWSP->N;
  //  npACB->ReqCmd[6]  = npACB->npMIF->EOT;              // @V104373
  npACB->ReqCmd[6]  = npACB->npMIF->Geo.SectorsPerTrack;  // @V104373
  npACB->ReqCmd[7]  = npACB->npMIF->RWGapLength;
  npACB->ReqCmd[8]  = 0xFF;

  /*---------------------------------------------------*/
  /* If the IORB is using RBA addressing, check if we  */
  /* are going to cross a track boundary. Adjust       */
  /* request size accordingly                          */
  /*---------------------------------------------------*/
  if ( !(npACB->OpFlags & OPF_CHS_ADDRESSING) )
  {
    if ( (npACB->ReqCmd[4] + npACB->SecCount) > npACB->npMIF->Geo.SectorsPerTrack )
    {
      npACB->SecCount   = npACB->npMIF->Geo.SectorsPerTrack
                                                       - npACB->ReqCmd[4] + 1;

    }
  }
  npACB->ReqDataLen = npACB->SecCount * pIORB->BlockSize;

  /*---------------------------------------------------*/
  /* Setup data transfer pointers for commands which   */
  /* require a data transfer.                          */
  /*---------------------------------------------------*/
  if ( npACB->OpFlags & (OPF_DATA_TO_DSKT | OPF_DATA_TO_HOST))
  {
    if ( npACB->OpFlags & OPF_DMA_BUF_REQD )
    {
      if ( npACB->ReqDataLen > npACB->DMABufLen )
      {
        _asm int 3
      }

      npACB->XferData.Mode = (npACB->OpFlags & OPF_DATA_TO_DSKT) ?
                                         SGLIST_TO_BUFFER : BUFFER_TO_SGLIST;

      npACB->XferData.cSGList       = pIORB->cSGList;
      npACB->XferData.pSGList       = pIORB->pSGList;
      npACB->XferData.pBuffer       = npACB->pDMABuf;
      npACB->XferData.numTotalBytes = npACB->ReqDataLen;
      npACB->XferData.iSGList       = 0;
      npACB->XferData.SGOffset      = 0;

      npACB->ReqDataBuf = npACB->DMABuf;
    }
    else
    {
      npACB->ReqDataBuf = pIORB->pSGList->ppXferBuf;
    }
  }

  /*-------------------------------------------------------*/
  /* Final command setup.                                  */
  /*                                                       */
  /* Select the controller op-code and prime the transfer  */
  /* for write requests if a buffered write is being       */
  /* done.                                                 */
  /*-------------------------------------------------------*/
  npACB->ReqCmdLen      = FC_LEN_READ_DATA;
  npACB->ReqResultsLen  = FR_LEN_READ_DATA;

  switch ( npACB->OpCode )
  {
    case IOCM_READ:
      npACB->ReqCmd[0]  = FC_READ_DATA;
      break;

    case IOCM_WRITE:
      if ( npACB->OpFlags & OPF_DMA_BUF_REQD )
      {
        f_ADD_XferBuffData( &npACB->XferData );
      }
      npACB->ReqCmd[0]  = FC_WRITE_DATA;
      break;

    case IOCM_READ_VERIFY:
      npACB->ReqCmd[0]  = FC_READ_DATA;
      break;

    default:
      _asm int 3

  }

  npACB->OSMNotifyRtn  = ExecuteIODone;

  StartOSMRequest( npACB, ACBRG_CMD_START );
}

/*------------------------------------------------------------------------*/
/*                                                                        */
/*  ExecIOPartial                                                         */
/*  -------------                                                         */
/*                                                                        */
/* Note: This routine does a minimal reinitialization for a FASTPATH      */
/*       IORB. This presumes that the first IORB in the sequence did      */
/*       a full initizliation and that the next IORB is closely related   */
/*       to the previous IORB.                                            */
/*                                                                        */
/*------------------------------------------------------------------------*/

VOID NEAR ExecIOPartial( NPACB npACB )
{
  PADDWSP               pWSP;
  PIORB_EXECUTEIO       pIORB;

  pIORB = (PIORB_EXECUTEIO) npACB->pIORB;
  pWSP  = (PADDWSP) npACB->pIORB->ADDWorkSpace;

  npACB->CHSAddr     = *(PCHS_ADDR) &pIORB->RBA;

  npACB->ReqCmd[1]  &= ~0x04;
  npACB->ReqCmd[1]  |= ((npACB->CHSAddr.Head & 1) << 2);
  npACB->ReqCmd[3]   = npACB->CHSAddr.Head;
  npACB->ReqCmd[4]   = npACB->CHSAddr.Sector;

  npACB->ReqDataBuf  = pIORB->pSGList->ppXferBuf;
  npACB->ReqDataLen  = pIORB->pSGList->XferBufLen;

  npACB->ReqCmd[5]   = pWSP->N;

  StartOSMRequest( npACB, ACBRG_CMD_ONLY );
}

/*-------------------------------*/
/*                               */
/*  ExecIODone                   */
/*  ----------                   */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID FAR ExecuteIODone( NPACB npACB )
{

  PIORB_EXECUTEIO pIORB;
  USHORT          PrevDataLen;
  USHORT          PrevSecCount;
  USHORT          OpFlagsS;

  OpFlagsS = npACB->OpFlags;

  pIORB = (PIORB_EXECUTEIO) npACB->pIORB;

  /*------------------------------------------------------*/
  /* If this operation just completed a VERIFY phase then */
  /* restore the original command information.            */
  /* If the VERIFY failed, then the normal error handling */
  /* will retry the underlying operation.                 */
  /*------------------------------------------------------*/
  if ( npACB->OpFlags & OPF_VERIFY_PHASE )
  {
    CompleteVerifyPhase( npACB );
  }

  /*------------------------------------*/
  /* If request completed without error */
  /*------------------------------------*/
  if ( npACB->CurStatus == REQS_NO_ERROR )
  {
    /*--------------------------------------------------*/
    /* If this operation requires a VERIFY phase then   */
    /* save the original command information and start  */
    /* the verify operation.                            */
    /* Processing of the underlying operation is        */
    /* suspended until the verify completes.            */
    /*--------------------------------------------------*/

    if ( npACB->OpFlags & OPF_VERIFY_REQD && !(OpFlagsS & OPF_VERIFY_PHASE) )

    {
      InitVerifyPhase( npACB );
      goto ExecuteIOExit;
    }

    /*------------------------------------*/
    /* Adjust request pointers            */
    /*------------------------------------*/
    npACB->SecRemain -= npACB->SecCount;
    npACB->RBA       += npACB->SecCount;
    PrevSecCount      = npACB->SecCount;
    PrevDataLen       = npACB->ReqDataLen;

    /*-----------------------------------------------*/
    /* If data transfer to host and DMA buffer used, */
    /* then transfer buffered data to user           */
    /*-----------------------------------------------*/
    if ( npACB->OpFlags & OPF_DMA_BUF_REQD )
    {
      if ( npACB->OpFlags & OPF_DATA_TO_HOST )
      {
        f_ADD_XferBuffData( &npACB->XferData );
      }
    }

    /*-----------------------------------------------*/
    /* If we needed to break-up the request due to   */
    /* cylinder crossings or buffer limitations,     */
    /* then continue the request.                    */
    /*-----------------------------------------------*/
    if ( npACB->SecRemain )
    {
      npACB->SecCount = npACB->SecRemain;

      /*-----------------------------------------------*/
      /* Update controller command CHS address         */
      /*-----------------------------------------------*/
      if (npACB->OpFlags & OPF_CHS_ADDRESSING )
      {
        npACB->CHSAddr.Sector += PrevSecCount;
      }
      else
      {
        f_ADD_ConvRBAtoCHS(  npACB->RBA,
                             &npACB->npMIF->Geo,
                             &npACB->CHSAddr           );
      }

      npACB->ReqCmd[1]  = npACB->UnitId | ((npACB->CHSAddr.Head & 1) << 2);
      npACB->ReqCmd[2]  = npACB->CHSAddr.Cylinder;
      npACB->ReqCmd[3]  = npACB->CHSAddr.Head;
      npACB->ReqCmd[4]  = npACB->CHSAddr.Sector;

      npACB->ReqCyl     = npACB->ReqCmd[2];

      /*-----------------------------------------------*/
      /* Adjust for cylinder boundary crossings        */
      /*-----------------------------------------------*/
      if ( !(npACB->OpFlags & IORB_CHS_ADDRESSING) )
      {
        if ( (npACB->ReqCmd[4] + npACB->SecCount) > npACB->npMIF->Geo.SectorsPerTrack )
        {
          npACB->SecCount   = npACB->npMIF->Geo.SectorsPerTrack - npACB->ReqCmd[4] + 1;
        }
      }
      npACB->ReqDataLen = npACB->SecCount * pIORB->BlockSize;

      /*-----------------------------------------------*/
      /* Set new data transfer parms                   */
      /*-----------------------------------------------*/
      if ( npACB->OpFlags & OPF_DMA_BUF_REQD )
      {
        npACB->XferData.numTotalBytes = npACB->ReqDataLen;

        /*-----------------------------------------------*/
        /* If data transfer to controller and DMA buffer */
        /* is used, then prime the DMA buffer.           */
        /*-----------------------------------------------*/
        if ( npACB->OpFlags & OPF_DATA_TO_DSKT )
        {
          f_ADD_XferBuffData( &npACB->XferData );
        }
      }
      else
      {
        npACB->ReqDataBuf += PrevDataLen;
      }

      StartOSMRequest( npACB, ACBRG_CMD_CONTINUE );
    }
    else
    {
      npACB->OState = ACBO_COMPLETE_IORB;
    }
  }

  /*-------------------------------------------------------*/
  /* If we are here, an I/O error occurred on the inital   */
  /* operation, or on a VERIFY phase. Depending on the     */
  /* type of ERROR, and the number of previous errors      */
  /* we may either retry the operation or return the       */
  /* IORB in ERROR.                                        */
  /*-------------------------------------------------------*/
  else
  {
    ExecuteIORetry( npACB );
  }

  if ( npACB->OState == ACBO_COMPLETE_IORB )
  {
    pIORB->BlocksXferred = pIORB->BlockCount - npACB->SecRemain;

    if ( npACB->Flags & ACBF_VIDEO_PAUSE )
    {
      DevHelp_VideoPause( VIDEO_PAUSE_OFF );
      npACB->Flags &= ~ACBF_VIDEO_PAUSE;
    }
  }

  ExecuteIOExit: ;
}


/*-------------------------------*/
/*                               */
/*  ExecuteIORetry               */
/*  --------------               */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID NEAR ExecuteIORetry( NPACB npACB )
{
  USHORT        ReqFlags = ACBRG_CMD_CONTINUE;

  if ( npACB->CurStatus == REQS_NOT_READY )
  {
    npACB->OState = ACBO_COMPLETE_IORB;
  }
  else if ( npACB->CurStatus == REQS_MEDIA_CHANGED )
  {
    npACB->OState = ACBO_COMPLETE_IORB;
  }
  else if ( npACB->CurStatus != REQS_NO_ERROR )
  {
    if ( (npACB->CurResults[0] & ST0_INT_CODE == ST0_ABNORMAL_TERM)
            && (npACB->CurResults[1] & ST1_OVERRUN)
               && !(npACB->Flags & ACBF_VIDEO_PAUSE)   )
    {
      DevHelp_VideoPause( VIDEO_PAUSE_ON );
      npACB->Flags |= ACBF_VIDEO_PAUSE;
    }

    if ( npACB->ExecIOError < MAX_EXECIO_ERROR )
    {
      npACB->ExecIOError++;
    }
    else if ( npACB->ExecIORecal < MAX_EXECIO_RECAL )
    {
      npACB->ExecIOError = 0;
      npACB->ExecIORecal++;

      ReqFlags = ACBRG_CMD_RECAL;
    }
    else
    {
      npACB->OState = ACBO_COMPLETE_IORB;
    }
  }

  if ( npACB->OState != ACBO_COMPLETE_IORB )
  {
    StartOSMRequest( npACB, ReqFlags );
  }
}


/*-------------------------------*/
/*                               */
/*  InitVerifyPhase              */
/*  ---------------              */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID NEAR InitVerifyPhase( NPACB npACB )
{
  PADDWSP       pWSP;

  pWSP = (PADDWSP) npACB->pIORB->ADDWorkSpace;

  memcpy( npACB->VerifyReqCmd, npACB->ReqCmd, npACB->ReqCmdLen );

  npACB->VerifyReqFlags      = npACB->ReqFlags;
  npACB->VerifyReqCmdLen     = npACB->ReqCmdLen;
  npACB->VerifyReqResultsLen = npACB->ReqResultsLen;
  npACB->VerifyReqDataLen    = npACB->ReqDataLen;
  npACB->VerifyReqDataBuf    = npACB->ReqDataBuf;
  npACB->VerifyOpFlags       = npACB->OpFlags;

  npACB->ReqCmd[0] = FC_READ_DATA;
  npACB->ReqCmd[1] = npACB->UnitId | ( (npACB->CHSAddr.Head & 1) << 2);
  npACB->ReqCmd[2] = npACB->ReqCyl;
  npACB->ReqCmd[3] = npACB->CHSAddr.Head;
  npACB->ReqCmd[4] = npACB->CHSAddr.Sector;
  npACB->ReqCmd[5] = pWSP->N;
//  npACB->ReqCmd[6] = npACB->npMIF->EOT;               // @V104373
  npACB->ReqCmd[6] = npACB->npMIF->Geo.SectorsPerTrack; // @V104373
  npACB->ReqCmd[7] = npACB->npMIF->RWGapLength;
  npACB->ReqCmd[8] = 0xFF;

  npACB->ReqCmdLen     = FC_LEN_READ_DATA;
  npACB->ReqResultsLen = FR_LEN_READ_DATA;
  npACB->ReqDataLen    = (128 << pWSP->N) * npACB->SecCount;
  npACB->ReqDataBuf    = 0l;

  npACB->OpFlags  |= OPF_VERIFY_PHASE;
  npACB->OpFlags  &= ~(OPF_DATA_TO_HOST | OPF_DATA_TO_DSKT);

  StartOSMRequest( npACB, ACBRG_CMD_ONLY );
}

/*-------------------------------*/
/*                               */
/*  CompleteVerifyPhase          */
/*  -------------------          */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID NEAR CompleteVerifyPhase( NPACB npACB )
{
  npACB->ReqFlags         = npACB->VerifyReqFlags;
  npACB->ReqCmdLen        = npACB->VerifyReqCmdLen;
  npACB->ReqResultsLen    = npACB->VerifyReqResultsLen;
  npACB->ReqDataLen       = npACB->VerifyReqDataLen;
  npACB->OpFlags          = npACB->VerifyOpFlags;
  npACB->ReqDataBuf       = npACB->VerifyReqDataBuf;

  memcpy( npACB->ReqCmd, npACB->VerifyReqCmd, npACB->ReqCmdLen );

  npACB->OpFlags &= ~OPF_VERIFY_PHASE;
}


/*------------------------------------------------------------------------*/
/*  IOCC_FORMAT                                                           */
/*  -----------                                                           */
/*------------------------------------------------------------------------*/

/*-------------------------------*/
/*                               */
/*  Format                       */
/*  ------                       */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID NEAR Format( NPACB npACB )
{
  PIORB_FORMAT          pIORB;
  PADDWSP               pWSP;
  PFORMAT_CMD_TRACK     pFMT;

  pIORB = (PIORB_FORMAT) npACB->pIORB;

  pWSP           = (PADDWSP) pIORB->iorbh.ADDWorkSpace;
  npACB->OpFlags = pWSP->Flags;

  /*---------------------------------------------------------*/
  /* If this IORB requires a DMABuffer and we currently      */
  /* do not have a buffer allocated, Start a context thread  */
  /* to allocate the buffer. Once the buffer is allocated    */
  /* the IORB will be restarted.                             */
  /*---------------------------------------------------------*/
  if ( (npACB->OpFlags & OPF_DMA_BUF_REQD) && !npACB->DMABuf )
  {
    npACB->Flags  |= ACBF_OSM_WAITSTATE;

    StartBufferAlloc( npACB );

    goto FormatExit;
  }

  npACB->OpCode  = ((PIORB) pIORB)->CommandModifier;

  pFMT  = (PFORMAT_CMD_TRACK) pIORB->pFormatCmd;

  npACB->ExecIOError = 0;
  npACB->ExecIORecal = 0;

  if ( pFMT->Flags & FF_VERIFY )
  {
    npACB->OpFlags |= OPF_VERIFY_REQD;
  }

  /*--------------------------------------*/
  /* Convert IORB->RBA to CHS addressing  */
  /*--------------------------------------*/
  npACB->RBA = pFMT->RBA;

  if (pIORB->iorbh.RequestControl & IORB_CHS_ADDRESSING )
  {
    npACB->CHSAddr = *(PCHS_ADDR) &pFMT->RBA;

    npACB->OpFlags |= OPF_CHS_ADDRESSING;
  }
  else
  {
    f_ADD_ConvRBAtoCHS(pFMT->RBA, &npACB->npMIF->Geo, &npACB->CHSAddr );
  }

  if ( pFMT->Flags & FF_FMTGAPLEN )
  {
    pWSP->N = BlkSizeToN( (USHORT) pFMT->BlockSize );
  }
  else
  {
    pWSP->N = BlkSizeToN( (USHORT) npACB->npMIF->Geo.BytesPerSector );
  }

  npACB->ReqCmdLen     = FC_LEN_FORMAT_TRACK;
  npACB->ReqResultsLen = FR_LEN_FORMAT_TRACK;

  npACB->ReqCmd[0]  = FC_FORMAT_TRACK;
  npACB->ReqCmd[1]  = npACB->UnitId | ((npACB->CHSAddr.Head & 1) << 2);
  npACB->ReqCmd[2]  = pWSP->N;
  npACB->ReqCmd[3]  = pFMT->cTrackEntries;
  npACB->ReqCmd[4]  = (pFMT->Flags & FF_FMTGAPLEN) ? pFMT->FmtGapLength :
                                                  npACB->npMIF->FmtGapLength;
  npACB->ReqCmd[5]  = npACB->npMIF->FmtFillByte;

  npACB->ReqCyl     = npACB->CHSAddr.Cylinder;

  npACB->ReqDataLen = pFMT->cTrackEntries << 2;

  npACB->SecCount   = pFMT->cTrackEntries;

  /*---------------------------------------------------*/
  /* Setup data transfer pointers for commands which   */
  /* require a data transfer.                          */
  /*---------------------------------------------------*/
  if ( npACB->OpFlags & OPF_DMA_BUF_REQD )
  {
    npACB->XferData.Mode          = SGLIST_TO_BUFFER;
    npACB->XferData.cSGList       = pIORB->cSGList;
    npACB->XferData.pSGList       = pIORB->pSGList;
    npACB->XferData.pBuffer       = npACB->pDMABuf;
    npACB->XferData.numTotalBytes = pFMT->cTrackEntries << 2;
    npACB->XferData.iSGList       = 0;
    npACB->XferData.SGOffset      = 0;

    npACB->ReqDataBuf = npACB->DMABuf;
    npACB->ReqDataLen = npACB->XferData.numTotalBytes;

    f_ADD_XferBuffData( &npACB->XferData );
  }
  else
  {
    npACB->ReqDataBuf = pIORB->pSGList->ppXferBuf;
    npACB->ReqDataLen = pIORB->pSGList->XferBufLen;
  }

  npACB->OSMNotifyRtn  = FormatDone;

  StartOSMRequest( npACB, ACBRG_CMD_START );

  FormatExit: ;
}

/*-------------------------------*/
/*                               */
/*  FormatDone                   */
/*  ----------                   */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/

VOID FAR FormatDone( NPACB npACB )
{
  USHORT        OpFlagsS;

  OpFlagsS = npACB->OpFlags;

  if ( npACB->OpFlags & OPF_VERIFY_PHASE )
  {
    CompleteVerifyPhase( npACB );
  }
  /*------------------------------------*/
  /* If request completed without error */
  /*------------------------------------*/
  if ( npACB->CurStatus == REQS_NO_ERROR )
  {
    if ( npACB->OpFlags & OPF_VERIFY_REQD && !(OpFlagsS & OPF_VERIFY_PHASE) )

    {
      InitVerifyPhase( npACB );
    }
    else
    {
      npACB->OState = ACBO_COMPLETE_IORB;
    }
  }
  else
  {
    ExecuteIORetry( npACB );
  }

  if ( npACB->OState == ACBO_COMPLETE_IORB )
  {
    if ( npACB->Flags & ACBF_VIDEO_PAUSE )
    {
      DevHelp_VideoPause( VIDEO_PAUSE_OFF );
      npACB->Flags &= ~ACBF_VIDEO_PAUSE;
    }
  }
}


/*------------------------------------------------------------------------*/
/*                                                                        */
/* Utility Routines                                                       */
/*                                                                        */
/*------------------------------------------------------------------------*/

/*-------------------------------------------------*/
/* CheckBufRequired                                */
/*                                                 */
/* This routine determines whether the passed      */
/* S/G list in the IORB meets ISA DMA constraints  */
/*                                                 */
/* The constraints are basically that the request  */
/* may not cross a 64KB physical address boundary  */
/* nor may a request cross or be above the         */
/* 16MB physical address boundary.                 */
/*-------------------------------------------------*/

USHORT NEAR CheckBufRequired( PIORB_EXECUTEIO pIORB )
{
  USHORT BufferRequired = 1;
  ULONG  ppBuf;
  ULONG  Len;

  /*--------------------------------------------------*/
  /* If user buffer has single extent and the entire  */
  /* buffer is below 16MB, then a blocking buffer     */
  /* is not required.                                 */
  /*--------------------------------------------------*/

  if ( pIORB->cSGList == 1 )
  {
    ppBuf    = pIORB->pSGList->ppXferBuf;
    Len      = pIORB->pSGList->XferBufLen;

    if ( (ppBuf + Len)           < C16MEG &&
        ((ppBuf & 0xffff) + Len) < C64KB       )
    {
      BufferRequired = 0;
    }
  }
  return( BufferRequired );
}


/*----------------------------------------------*/
/* BlkSizeToN                                   */
/*                                              */
/* Converts a blocksize (bytes) to Sector Size  */
/* Code (N).                                    */
/*----------------------------------------------*/

USHORT FAR BlkSizeToN( USHORT BlkSize )
{
  USHORT     BlkTmp;
  USHORT     n;

  /*-----------------------------------------------------*/
  /* Scan bits 7->MAX_N for 1. Report bit position (-7), */
  /* i.e. 0x0080 = (0), 0x0100 = (1), etc                */
  /*-----------------------------------------------------*/
  BlkTmp = BlkSize >> 7;
  for ( n=0; n < MAX_N; n++, BlkTmp >>= 1 )
  {
    if (BlkTmp & 1) break;
  }

  /*---------------------------------------------------*/
  /* BlkSize must be a power of 2. If any bit other    */
  /* than the expected 2^(N+7) value is on, then the   */
  /* original blocksize was invalid                    */
  /*---------------------------------------------------*/

  if ( BlkSize & ~(0x0001 << (n+7))  ) n = -1;

  return ( n );
}

