/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "%w% %e%";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = FL2IO.C
 *
 * DESCRIPTIVE NAME = IBM2FLPY.ADD - Adapter Driver for ABIOS Diskette
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : Routines to implement read, write and verify.
 *
 *
 *
*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include <os2.h>
#include <dhcalls.h>
#include <strat2.h>     /* needed to keep reqpkt.h happy */
#include <reqpkt.h>
#include <scb.h>        /* needed to keey abios.h happy */
#include <abios.h>
#include <iorb.h>
#include <addcalls.h>
#include "fl2def.h"
#include "fl2proto.h"
#include "fl2data.h"



/*****************************************************************************/
/*                                                                           */
/*   Routine     : IO                                                        */
/*                                                                           */
/*   Description : This routine checks to see a non-standard sector          */
/*                 size is being read. If so, this routine establishes       */
/*                 the requested sector size.                                */
/*                                                                           */
/*                 IOPhase1 is called to continue processing the request.    */
/*                                                                           */
/*****************************************************************************/

VOID NEAR IO()
{
   NPABRB_DSKT_SETDEVPARMS pRB = (NPABRB_DSKT_SETDEVPARMS) RequestBlock;
   USHORT                  SectorSizeCode;

   /*---------------------------------------------------*/           /*@V82589*/
   /* Changes for Defect 82589                          */           /*@V82589*/
   /*                                                   */           /*@V82589*/
   /* 1.) Issue Set Device Parms if request sector      */           /*@V82589*/
   /*     size differs from current device sector       */           /*@V82589*/
   /*     size.                                         */           /*@V82589*/
   /*---------------------------------------------------*/           /*@V82589*/

   SectorSizeCode = BlkSizeToN(((PIORB_EXECUTEIO)pHeadIORB)->BlockSize);

   if ( SectorSizeCode == -1 )
      {
         pHeadIORB->Status   |= IORB_ERROR;
         pHeadIORB->ErrorCode = IOERR_MEDIA_NOT_SUPPORTED;
         IORBDone();
         return;
      }


   if ( Drive[pHeadIORB->UnitHandle].SectorSizeCode != SectorSizeCode )
      {
         Drive[pHeadIORB->UnitHandle].SectorSizeCode = SectorSizeCode;

         pRB->abrbh.Function = ABFC_SET_DEVICE_PARMS;
         pRB->abrbh.Unit     = pHeadIORB->UnitHandle;
         pRB->abrbh.RC       = ABRC_START;
         pRB->BlockSize      = SectorSizeCode;
         pRB->ReadGap        = Drive[pHeadIORB->UnitHandle].ReadGap;
         pRB->DataLen        = Drive[pHeadIORB->UnitHandle].DataLen;

         CompletionRoutine   = IOPhase1;
         Retry = 0;
         Stage = ABIOS_EP_START;
         NextStage();
      }
   else
      {
         pRB->abrbh.RC = ABRC_COMPLETEOK;
         IOPhase1();
      }

}

/*****************************************************************************/
/*                                                                           */
/*   Routine     : IOPhase1                                                  */
/*                                                                           */
/*   Description : This routine insures that the media has not been          */
/*                 changed since the last access and that the media          */
/*                 desity has been established.                              */
/*                                                                           */
/*                 DetermineMedia is called to establish the media type.     */
/*                                                                           */
/*                 RWV is called to continue processing the request.         */
/*                                                                           */
/*****************************************************************************/


VOID FAR IOPhase1()
{
   NPABRBH pRB = (NPABRBH) RequestBlock;

   /*---------------------------------------------------*/           /*@V82589*/
   /* Changes for Defect 82589                          */           /*@V82589*/
   /*                                                   */           /*@V82589*/
   /* 1.) Call common routine which does media          */           /*@V82589*/
   /*     media determination.                          */           /*@V82589*/
   /*                                                   */           /*@V82589*/
   /* Note: DetermineMedia will 'remember' if           */           /*@V82589*/
   /*       unformatted media is detected and will      */           /*@V82589*/
   /*       immediately return an error indication      */           /*@V82589*/
   /*       until the media is changed.                 */           /*@V82589*/
   /*                                                   */           /*@V82589*/
   /*       This helps speed-up formats on unformatted  */           /*@V82589*/
   /*       media since the ifs mechanism insists on    */           /*@V82589*/
   /*       reading the media several times before      */           /*@V82589*/
   /*       formatting begins.                          */           /*@V82589*/
   /*                                                   */           /*@V82589*/
   /*---------------------------------------------------*/           /*@V82589*/

   if ( pRB->RC == ABRC_COMPLETEOK )
      {
         DetermineMedia( RWV );
      }
   else
      {
         pHeadIORB->Status   |= IORB_ERROR;
         pHeadIORB->ErrorCode = TranslateErrorCode( pRB->RC );
         IORBDone();
         return;
      }
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : RWV                                                       */
/*                                                                           */
/*   Description : This routine does the initial setup of the ABIOS          */
/*                 request block and the XferData structure for the          */
/*                 read, write or verify.                                    */
/*                                                                           */
/*****************************************************************************/

VOID FAR RWV()
{
   PIORB_EXECUTEIO pIORB = (PIORB_EXECUTEIO)pHeadIORB;
   NPABRB_DSKT_RWV pRB   = (NPABRB_DSKT_RWV)RequestBlock;
   union
      {
         CHS_ADDR CHS;
         ULONG    RBA;
      } Addr;

   if ( pHeadIORB->ErrorCode )
      {
         pHeadIORB->Status |= IORB_ERROR;

         StartTimer( pHeadIORB->UnitHandle,
                     Drive[pHeadIORB->UnitHandle].MotorOffDelay ,TurnOffMotor );

         IORBDone();
         return;
      }

   /* Calculate initial CHS */
   if ( pHeadIORB->RequestControl & IORB_CHS_ADDRESSING )
      Addr.RBA = pIORB->RBA;
   else
      {
         if ( f_ADD_ConvRBAtoCHS( pIORB->RBA,
              &(Drive[pHeadIORB->UnitHandle].Geometry[MEDIA]), (PCHS_ADDR)&Addr ) )
            {
               pHeadIORB->Status   |= IORB_ERROR;
               pHeadIORB->ErrorCode = IOERR_RBA_ADDRESSING_ERROR;
               IORBDone();
               return;
            }
      }

   pRB->Cylinder        = Addr.CHS.Cylinder;
   pRB->Head            = Addr.CHS.Head;
   pRB->Sector          = Addr.CHS.Sector;

   pRB->abrbh.Unit      = pHeadIORB->UnitHandle;
   pRB->ppIObuffer      = ppDMABuffer;

   XferData.cSGList     = pIORB->cSGList;
   XferData.pSGList     = pIORB->pSGList;
   XferData.pBuffer     = pDMABuffer;
   XferData.iSGList     = 0;
   XferData.SGOffset    = 0;

   pIORB->BlocksXferred = 0;

   NextRWV();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : NextRWV                                                   */
/*                                                                           */
/*   Description : This routine is called repeatively during a large         */
/*                 IORB read, write or verify request to break the           */
/*                 request up into smaller chunks.                           */
/*                                                                           */
/*                 An IORB request may need to be broken up into several     */
/*                 ABIOS requests, because ABIOS can read at most one        */
/*                 cylinder at a time.  A single ABIOS request can not       */
/*                 cross a cylinder boundry.  If the media geometry is       */
/*                 logical, then an ABIOS request can not cross a head       */
/*                 boundry.  Also, if the DMA buffer is small, then we       */
/*                 may have to break the request up into buffer size         */
/*                 chunks.                                                   */
/*                                                                           */
/*                 If the request is a write request, then this routine      */
/*                 transfers the write data from the scatter/gather list     */
/*                 to the DMA buffer.                                        */
/*                                                                           */
/*   Input  : pRB->Cylinder  \                                               */
/*            pRB->Head       > location of next sector to RWV               */
/*            pRB->Sector    /                                               */
/*                                                                           */
/*            pIORB->BlockCount       > difference is sectors left to RWV    */
/*            pIORB->BlocksXferred   /                                       */
/*                                                                           */
/*****************************************************************************/

VOID NEAR NextRWV()
{
   PIORB_EXECUTEIO pIORB = (PIORB_EXECUTEIO)pHeadIORB;
   NPABRB_DSKT_RWV pRB   = (NPABRB_DSKT_RWV)RequestBlock;
   USHORT SectorsLeft,SectorsPerBuffer;
   NPGEOMETRY pGeometry;

   pGeometry = &(Drive[pHeadIORB->UnitHandle].Geometry[MEDIA]);

   if ( pHeadIORB->RequestControl & IORB_CHS_ADDRESSING )
      {
         SectorCnt = pIORB->BlockCount;
      }
   else
      {
         if ( Drive[pHeadIORB->UnitHandle].Flags.LogicalMedia )
            /* SectorCnt = max possible sectors so head boundry is not crossed */
            SectorCnt = pGeometry->SectorsPerTrack + 1 - pRB->Sector;
         else
            /* SectorCnt = max possible sectors so cylinder boundry is not crossed */
            SectorCnt = ((pGeometry->NumHeads - pRB->Head) * pGeometry->SectorsPerTrack) + 1 - pRB->Sector;
      }

   /* Don't do more sectors than can fit into the DMA buffer */
   SectorsPerBuffer = BuffSize / pIORB->BlockSize;
   if ( SectorCnt > SectorsPerBuffer ) SectorCnt = SectorsPerBuffer;

   /* Don't do more sectors than requested */
   SectorsLeft = pIORB->BlockCount - pIORB->BlocksXferred;
   if ( SectorCnt > SectorsLeft ) SectorCnt = SectorsLeft;

   switch( pHeadIORB->CommandModifier )
      {
         case IOCM_WRITE:
         case IOCM_WRITE_VERIFY:
               {
                  /* Transfer data from the S/G List to the DMA buffer */
                  XferData.Mode = SGLIST_TO_BUFFER;
                  XferData.numTotalBytes = ((ULONG)SectorCnt) * pIORB->BlockSize;
                  if ( f_ADD_XferBuffData( &XferData ) )
                     {
                        pHeadIORB->Status   |= IORB_ERROR;
                        pHeadIORB->ErrorCode = IOERR_CMD_SGLIST_BAD;
                        StartTimer( pHeadIORB->UnitHandle,
                           Drive[pHeadIORB->UnitHandle].MotorOffDelay ,TurnOffMotor );
                        IORBDone();
                        return;
                     }

                  pRB->abrbh.Function = ABFC_DSKT_WRITE;
               }
            break;

         case IOCM_READ:
            pRB->abrbh.Function = ABFC_DSKT_READ;
            break;

         case IOCM_READ_VERIFY:
            pRB->abrbh.Function = ABFC_DSKT_VERIFY;
            break;
      }

   NextRWVStep();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : NextRWVStep                                               */
/*                                                                           */
/*   Description : This routine does the final setup before calling          */
/*                 NextStage to do the read, write or verify.  If the        */
/*                 operation is a write/verify then this routine will        */
/*                 be called a second time to do the verify (or read).       */
/*                                                                           */
/*****************************************************************************/

VOID NEAR NextRWVStep()
{
   NPABRB_DSKT_RWV pRB = (NPABRB_DSKT_RWV)RequestBlock;

   pRB->cSectors   = SectorCnt;
   pRB->abrbh.RC   = ABRC_START;
   pRB->Reserved_1 = 0;           /* +10H reserved field           */
   pRB->Reserved_2 = 0L;          /* +16H and +18H reserved fields */
   pRB->Reserved_3 = 0;           /* +1EH reserved field           */

   CompletionRoutine = RWVComplete;
   Drive[pRB->abrbh.Unit].Flags.MotorOn = TRUE;
   Retry = 0;
   Stage = ABIOS_EP_START;
   NextStage();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : RWVComplete                                               */
/*                                                                           */
/*   Description : This routine is called when the ABIOS read, write or      */
/*                 verify operation completes.                               */
/*                                                                           */
/*                 If the operation was a read then data is tranferred       */
/*                 from the DMA buffer to the scatter/gather list.           */
/*                                                                           */
/*                 If the operation is a write/verify and the write step     */
/*                 has just completed successfully, then this routine        */
/*                 sets up the ABIOS request block for the verify step.      */
/*                 If read back is required, then the verify is              */
/*                 actually a read back.  When the read back completes,      */
/*                 then the last byte of each sector read back is            */
/*                 compared to what should have been written.  If it         */
/*                 does not match then the write is done again.              */
/*                                                                           */
/*                 This routine updates the BlocksXferred field in the       */
/*                 IORB.  If the IORB request is not finished, then          */
/*                 the CHS for the next block of sectors is calculated       */
/*                 and NextRWV is called to read, write or verify them.      */
/*                                                                           */
/*****************************************************************************/

VOID FAR RWVComplete()
{
   PIORB_EXECUTEIO pIORB = (PIORB_EXECUTEIO)pHeadIORB;
   NPABRB_DSKT_RWV pRB   = (NPABRB_DSKT_RWV)RequestBlock;
   NPGEOMETRY pGeometry;
   USHORT cTracks,x;
   PBYTE pDataWritten,pDataReadBack;

   pGeometry = &(Drive[pHeadIORB->UnitHandle].Geometry[MEDIA]);

   if ( pHeadIORB->CommandModifier == IOCM_READ )
      {
         /* Transfer data from the DMA buffer to the S/G List */
         XferData.Mode = BUFFER_TO_SGLIST;
         XferData.numTotalBytes = ((ULONG)pRB->cSectors) * pIORB->BlockSize;
         if ( f_ADD_XferBuffData( &XferData ) )
            {
               pHeadIORB->Status   |= IORB_ERROR;
               pHeadIORB->ErrorCode = IOERR_CMD_SGLIST_BAD;
               StartTimer( pHeadIORB->UnitHandle,
                  Drive[pHeadIORB->UnitHandle].MotorOffDelay ,TurnOffMotor );
               IORBDone();
               return;
            }
      }
   else if ( pHeadIORB->CommandModifier == IOCM_WRITE_VERIFY )
      {
         if ( pRB->abrbh.RC == ABRC_COMPLETEOK )
            {
               if ( pRB->abrbh.Function == ABFC_DSKT_WRITE )
                  {
                     if ( Drive[pHeadIORB->UnitHandle].Flags.ReadBackReq )
                        {
                           pRB->ppIObuffer     = ppReadBackBuffer;
                           pRB->abrbh.Function = ABFC_DSKT_READ;
                        }
                     else /* Read back not required; just do a verify */
                        {
                           pRB->abrbh.Function = ABFC_DSKT_VERIFY;
                        }
                     NextRWVStep();
                     return;
                  }
               else if ( pRB->abrbh.Function == ABFC_DSKT_READ )
                  {
                     pRB->ppIObuffer     = ppDMABuffer;
                     pRB->abrbh.Function = ABFC_DSKT_WRITE;

                     /* Check the last byte of each sector */
                     pDataWritten   = pDMABuffer      + pIORB->BlockSize - 1;
                     pDataReadBack  = pReadBackBuffer + pIORB->BlockSize - 1;
                     for ( x=0; x<pRB->cSectors; x++ )
                        {
                           if ( *pDataReadBack != *pDataWritten )
                              {
                                 NextRWVStep();  /* Write it again */
                                 return;
                              }
                           pDataWritten  += pIORB->BlockSize;
                           pDataReadBack += pIORB->BlockSize;
                        }
                  }
               else pRB->abrbh.Function = ABFC_DSKT_WRITE;  /* Verify done */
            }
      }

   pIORB->BlocksXferred += pRB->cSectors;

   if ( pRB->abrbh.RC == ABRC_COMPLETEOK )
      {
         if ( pIORB->BlocksXferred < pIORB->BlockCount ) /* if not yet done */
            {
               if ( pHeadIORB->RequestControl & IORB_CHS_ADDRESSING )
                  {
                     pRB->Sector += SectorCnt;
                  }
               else
                  {
                     /* Calculate new CHS */
                     cTracks        =  (pRB->Sector-1+pRB->cSectors)/pGeometry->SectorsPerTrack;
                     pRB->Sector    = ((pRB->Sector-1+pRB->cSectors)%pGeometry->SectorsPerTrack)+1;
                     pRB->Cylinder +=  (pRB->Head + cTracks) / pGeometry->NumHeads;
                     pRB->Head      =  (pRB->Head + cTracks) % pGeometry->NumHeads;
                  }
               NextRWV();  /* Do next RWV */
               return;
            }
      }
   else
      {
         pHeadIORB->Status   |= IORB_ERROR;
         pHeadIORB->ErrorCode = TranslateErrorCode( pRB->abrbh.RC );
      }

   if ( pHeadIORB->ErrorCode == IOERR_MEDIA_CHANGED )
      Drive[pHeadIORB->UnitHandle].Flags.UnknownMedia = TRUE;

   StartTimer( pHeadIORB->UnitHandle,
              Drive[pHeadIORB->UnitHandle].MotorOffDelay ,TurnOffMotor );

   IORBDone();
}



/*****************************************************************************/
/*                                                                           */
/*   Routine     : BlkSizeToN                                                */
/*                                                                           */
/*   Description : This routine 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 < 7; 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 );
}

