/*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 = "src/dev/dasd/ibm/ibm2flpy/fl2iorb.c, flp2, r207, 8.005p 93/04/17";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = FL2IORB.C
 *
 * DESCRIPTIVE NAME = IBM2FLPY.ADD - Adapter Driver for ABIOS Diskette
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 *
*/
#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     : NextIORB                                                  */
/*                                                                           */
/*   Description : This routine starts the processing of the IORB at         */
/*                 the head of the list.  This routine checks for            */
/*                 valid unit handle and that the unit is allocated.         */
/*                 The IORB is then routed to the appropriate                */
/*                 processsing routine.                                      */
/*                                                                           */
/*****************************************************************************/

VOID FAR NextIORB()
{
   if ( pHeadIORB->CommandCode != IOCC_CONFIGURATION )
      {
         /* Verify that UnitHandle is valid */
         if ( pHeadIORB->UnitHandle >= UnitCnt )
            {
               /* UnitHandle is out of legal range */
               pHeadIORB->Status   |= IORB_ERROR;
               pHeadIORB->ErrorCode = IOERR_CMD_SYNTAX;
               IORBDone();
               return;
            }

         /* Verify that unit is allocated */
         if ( pHeadIORB->CommandCode != IOCC_UNIT_CONTROL )
            {
               if ( !Drive[pHeadIORB->UnitHandle].Flags.Allocated )
                  {
                     /* Unit not allocated */
                     pHeadIORB->Status   |= IORB_ERROR;
                     pHeadIORB->ErrorCode = IOERR_UNIT_NOT_ALLOCATED;
                     IORBDone();
                     return;
                  }
            }
      }

   /* Route the IORB to the proper routine */
   switch( pHeadIORB->CommandCode )
      {
         case IOCC_CONFIGURATION:
            switch( pHeadIORB->CommandModifier )
               {
                  case IOCM_GET_DEVICE_TABLE: GetDeviceTable();  break;
                  case IOCM_COMPLETE_INIT:    CompleteInit();    break;
                  default:                    CmdNotSupported(); break;
               }
            break;

         case IOCC_UNIT_CONTROL:
            switch( pHeadIORB->CommandModifier )
               {
                  case IOCM_ALLOCATE_UNIT:   AllocateUnit();    break;
                  case IOCM_DEALLOCATE_UNIT: DeallocateUnit();  break;
                  case IOCM_CHANGE_UNITINFO: ChangeUnitInfo();  break;
                  default:                   CmdNotSupported(); break;
               }
            break;

         case IOCC_GEOMETRY:
            switch( pHeadIORB->CommandModifier )
               {
                  case IOCM_GET_MEDIA_GEOMETRY:   GetMediaGeometry();   break;
                  case IOCM_SET_MEDIA_GEOMETRY:   SetMediaGeometry();   break;
                  case IOCM_GET_DEVICE_GEOMETRY:  GetDeviceGeometry();  break;
                  case IOCM_SET_LOGICAL_GEOMETRY: SetLogicalGeometry(); break;
                  default:                        CmdNotSupported();    break;
               }
            break;

         case IOCC_EXECUTE_IO:
            switch( pHeadIORB->CommandModifier )
               {
                  case IOCM_READ:          IO();              break;
                  case IOCM_READ_VERIFY:   IO();              break;
                  case IOCM_WRITE:         IO();              break;
                  case IOCM_WRITE_VERIFY:  IO();              break;
                  default:                 CmdNotSupported(); break;
               }
            break;

         case IOCC_FORMAT:
            switch( pHeadIORB->CommandModifier )
               {
                  case IOCM_FORMAT_TRACK: Format();          break;
                  default:                CmdNotSupported(); break;
               }
            break;

         case IOCC_UNIT_STATUS:
            switch( pHeadIORB->CommandModifier )
               {
                  case IOCM_GET_UNIT_STATUS:      GetUnitStatus();      break;
                  case IOCM_GET_CHANGELINE_STATE: GetChangelineState(); break;
                  case IOCM_GET_MEDIA_SENSE:      GetMediaSense();      break;
                  case IOCM_GET_LOCK_STATUS:      GetUnitLockStatus();  break;
                  default:                        CmdNotSupported();    break;
               }
            break;

         case IOCC_DEVICE_CONTROL:                                   /* F26083 */
            switch ( pHeadIORB->CommandModifier ) {                  /* F26083 */
            case IOCM_LOCK_MEDIA:                                    /* F26083 */
            case IOCM_UNLOCK_MEDIA:                                  /* F26083 */
            case IOCM_EJECT_MEDIA:                                   /* F26083 */
                            LkUnlkEjt();       break;                /* F26083 */
                 default:   CmdNotSupported(); break;                /* F26083 */
            } /* endswitch */                                        /* F26083 */
            break;                                                   /* F26083 */

         default: CmdNotSupported(); break;
      }
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetDeviceTable                                            */
/*                                                                           */
/*   Description : This routine builds a device table in the buffer          */
/*                 specified by the caller.                                  */
/*                                                                           */
/*                 Note: If a filter ADD has previously issued a             */
/*                       change unit info IORB, then the changed unit        */
/*                       information is placed in the device table.          */
/*                                                                           */
/*****************************************************************************/

VOID NEAR GetDeviceTable()
{
   PIORB_CONFIGURATION pIORB = (PIORB_CONFIGURATION)pHeadIORB;
   USHORT LengthNeeded,i;
   ADAPTERINFO far *pADPT;

   LengthNeeded = sizeof(DEVICETABLE) + sizeof(ADAPTERINFO) +
                  ((UnitCnt-1) * sizeof(UNITINFO));

   if ( pIORB->DeviceTableLen < LengthNeeded )
      {
         /* Not enough storage */
         pHeadIORB->Status |= IORB_ERROR;
         pHeadIORB->ErrorCode = IOERR_CMD_SYNTAX;
         IORBDone();
         return;
      }

   pIORB->pDeviceTable->ADDLevelMajor = ADD_LEVEL_MAJOR;
   pIORB->pDeviceTable->ADDLevelMinor = ADD_LEVEL_MINOR;
   pIORB->pDeviceTable->ADDHandle     = ADDHandle;
   pIORB->pDeviceTable->TotalAdapters = 1;
   pIORB->pDeviceTable->pAdapter[0]   =
      (NPADAPTERINFO)( OFFSETOF(pIORB->pDeviceTable) + sizeof(DEVICETABLE) );

   SELECTOROF(pADPT) = SELECTOROF(pIORB->pDeviceTable);
   OFFSETOF  (pADPT) = (USHORT)(pIORB->pDeviceTable->pAdapter[0]);

   /* Copy the adapter name */
   for ( i=0; (pADPT->AdapterName[i]=AdapterName[i])!=0; i++ )
      if ( i == 17 ) break;

   pADPT->AdapterUnits    = UnitCnt;
   pADPT->AdapterDevBus   = AI_DEVBUS_FLOPPY;
   pADPT->AdapterIOAccess = AI_IOACCESS_DMA_SLAVE;
   pADPT->AdapterHostBus  = AI_HOSTBUS_uCHNL | AI_BUSWIDTH_32BIT;
   pADPT->AdapterSCSITargetID = 0;
   pADPT->AdapterSCSILUN  = 0;
   pADPT->AdapterFlags    = AF_CHS_ADDRESSING;
   pADPT->MaxHWSGList     = 0;
   pADPT->MaxCDBTransferLength = 0L;

   for ( i=0; i<UnitCnt; i++ )
      {
         if ( Drive[i].pUnitInfo == NULL )  /* If unit info was not changed */
            {
               pADPT->UnitInfo[i].AdapterIndex     = 0;
               pADPT->UnitInfo[i].UnitIndex        = i;
               pADPT->UnitInfo[i].UnitHandle       = i;
               pADPT->UnitInfo[i].FilterADDHandle  = 0;
               pADPT->UnitInfo[i].UnitType         = UIB_TYPE_DISK;
               pADPT->UnitInfo[i].QueuingCount     = 1;
               pADPT->UnitInfo[i].UnitSCSITargetID = 0;
               pADPT->UnitInfo[i].UnitSCSILUN      = 0;

               pADPT->UnitInfo[i].UnitFlags = UF_REMOVABLE | UF_NOSCSI_SUPT;

               if ( Drive[i].Flags.HasChangeLine )
                  pADPT->UnitInfo[i].UnitFlags |= UF_CHANGELINE;

               if ( i == 0 )          /* First drive */
                  {
                     pADPT->UnitInfo[i].UnitFlags |= UF_A_DRIVE;
                     if ( UnitCnt == 1 )  /* Single drive is both A: and B: */
                        pADPT->UnitInfo[i].UnitFlags |= UF_B_DRIVE;
                  }
               else if ( i == 1 )     /* Second drive */
                  pADPT->UnitInfo[i].UnitFlags |= UF_B_DRIVE;
            }
         else  /* Pass back the changed unit info */
            {
               pADPT->UnitInfo[i].AdapterIndex     = Drive[i].pUnitInfo->AdapterIndex;
               pADPT->UnitInfo[i].UnitIndex        = Drive[i].pUnitInfo->UnitIndex;
               pADPT->UnitInfo[i].UnitHandle       = Drive[i].pUnitInfo->UnitHandle;
               pADPT->UnitInfo[i].FilterADDHandle  = Drive[i].pUnitInfo->FilterADDHandle;
               pADPT->UnitInfo[i].UnitType         = Drive[i].pUnitInfo->UnitType;
               pADPT->UnitInfo[i].QueuingCount     = Drive[i].pUnitInfo->QueuingCount;
               pADPT->UnitInfo[i].UnitSCSITargetID = Drive[i].pUnitInfo->UnitSCSITargetID;
               pADPT->UnitInfo[i].UnitSCSILUN      = Drive[i].pUnitInfo->UnitSCSILUN;
               pADPT->UnitInfo[i].UnitFlags        = Drive[i].pUnitInfo->UnitFlags;
            }
      }

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : AllocateUnit                                              */
/*                                                                           */
/*   Description : If unit not already allocated then mark it as             */
/*                 allocated now.                                            */
/*                                                                           */
/*****************************************************************************/

VOID NEAR AllocateUnit()
{
   if ( Drive[pHeadIORB->UnitHandle].Flags.Allocated )
      {
         /* Unit already allocated */
         pHeadIORB->Status   |= IORB_ERROR;
         pHeadIORB->ErrorCode = IOERR_UNIT_ALLOCATED;
      }
   else Drive[pHeadIORB->UnitHandle].Flags.Allocated = TRUE;

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : DeallocateUnit                                            */
/*                                                                           */
/*   Description : If unit is allocted then mark it as available.            */
/*                                                                           */
/*****************************************************************************/

VOID NEAR DeallocateUnit()
{
   if ( !Drive[pHeadIORB->UnitHandle].Flags.Allocated )
      {
         /* Unit not allocated */
         pHeadIORB->Status   |= IORB_ERROR;
         pHeadIORB->ErrorCode = IOERR_UNIT_NOT_ALLOCATED;
      }
   else Drive[pHeadIORB->UnitHandle].Flags.Allocated = FALSE;

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : ChangeUnitInfo                                            */
/*                                                                           */
/*   Description : Save the pointer to the new unit information.  This       */
/*                 unit information is used when building subsequent         */
/*                 device tables.                                            */
/*                                                                           */
/*****************************************************************************/

VOID NEAR ChangeUnitInfo()
{
   PIORB_UNIT_CONTROL pIORB = (PIORB_UNIT_CONTROL)pHeadIORB;

   if ( !Drive[pHeadIORB->UnitHandle].Flags.Allocated )
      {
         /* Unit not allocated */
         pHeadIORB->Status   |= IORB_ERROR;
         pHeadIORB->ErrorCode = IOERR_UNIT_NOT_ALLOCATED;
         IORBDone();
         return;
      }

   /* Save the Unit Info pointer */
   Drive[pHeadIORB->UnitHandle].pUnitInfo = pIORB->pUnitInfo;

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetUnitStatus                                             */
/*                                                                           */
/*   Description : PS/2 units are always powered on and ready for use.       */
/*                                                                           */
/*****************************************************************************/

VOID NEAR GetUnitStatus()
{
   PIORB_UNIT_STATUS pIORB = (PIORB_UNIT_STATUS)pHeadIORB;

   NPABRB_DSKT_RWV pRB = (NPABRB_DSKT_RWV)RequestBlock;

   pIORB->UnitStatus = US_POWER;

      /* Do a dummy verify to see if media in drive. */

   pRB->abrbh.Function  = ABFC_DSKT_VERIFY;                          /*@V51531*/
   pRB->abrbh.Unit      = pIORB->iorbh.UnitHandle;                   /*@V51531*/
   pRB->Cylinder        = 0;                                         /*@V51531*/
   pRB->Head            = 0;                                         /*@V51531*/
   pRB->Sector          = 1;                                         /*@V51531*/
   pRB->cSectors        = 1;                                         /*@V51531*/
   pRB->abrbh.RC        = ABRC_START;                                /*@V51531*/
   pRB->Reserved_2      = 0;     /* +16H reserved field  */          /*@V51531*/
   pRB->Reserved_3      = 0;     /* +1EH reserved field  */          /*@V51531*/
   CompletionRoutine = GetUnitStatusComplete;                        /*@V51531*/
   Retry = 0;                                                        /*@V51531*/
   Stage = ABIOS_EP_START;                                           /*@V51531*/
   NextStage();                                                      /*@V51531*/

}

/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetUnitStatusComplete                                     *//*@V51531*/
/*                                                                           *//*@V51531*/
/*   Description : This routine is called to determine if media is in the    *//*@V51531*/
/*                 drive.                                                    *//*@V51531*/
/*                                                                           */
/*****************************************************************************/

VOID FAR GetUnitStatusComplete()                                     /*@V51531*/
{
   PIORB_UNIT_STATUS  pIORB = ( PIORB_UNIT_STATUS)pHeadIORB;
   NPABRB_DSKT_CONTROL pRB = (NPABRB_DSKT_CONTROL)RequestBlock;

   if ( pRB->abrbh.RC == ABRC_DSKT_MEDIA_NOT_PRESENT )
       pIORB->UnitStatus &= ~US_READY;
   else {
       pIORB->UnitStatus |= US_READY;
       if( !(pRB->abrbh.RC == ABRC_COMPLETEOK) )
          pIORB->iorbh.ErrorCode = TranslateErrorCode( pRB->abrbh.RC );

   }

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

   IORBDone();
}

/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetUnitLockStatus                                         */
/*                                                                           */
/*   Description : PS/2 units are always powered on and ready for use.       */
/*                                                                           */
/*****************************************************************************/

VOID NEAR GetUnitLockStatus()
{
   PIORB_UNIT_STATUS pIORB = (PIORB_UNIT_STATUS)pHeadIORB;

   NPABRB_DSKT_CONTROL pRB   = (NPABRB_DSKT_CONTROL)RequestBlock;    /*@V51531*/

   if (Drive[pIORB->iorbh.UnitHandle].LockState==LOCK_STATE_UNKNOWN) { /*@V54106*/
      pRB->abrbh.Function  = ABFC_DSKT_STATUS;                       /*@V51531*/
      pRB->abrbh.Unit      = pIORB->iorbh.UnitHandle;                /*@V51531*/
      pRB->abrbh.RC        = ABRC_START;                             /*@V51531*/
      CompletionRoutine    = GetUnitLockStatusComplete;              /*@V51531*/
      Retry = 0;                                                     /*@V51531*/
      Stage = ABIOS_EP_START;                                        /*@V51531*/
      NextStage();                                                   /*@V51531*/
   } else {                                                          /*@V54106*/
      if (Drive[pIORB->iorbh.UnitHandle].LockState==LOCKED)          /*@V54106*/
         pIORB->UnitStatus |= US_LOCKED;                             /*@V54106*/
      else                                                           /*@V54106*/
         pIORB->UnitStatus &= ~US_LOCKED;                            /*@V54106*/
      IORBDone();                                                    /*@V54106*/
   } /* endif */                                                     /*@V54106*/
}

/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetUnitLockStatusComplete                                 *//*@V51531*/
/*                                                                           *//*@V51531*/
/*   Description : This routine is called when the ABIOS Get Lock Status     *//*@V51531*/
/*                 command completes.  The IORB is updated with the drive    *//*@V51531*/
/*                 lock status in the drive.                                 *//*@V51531*/
/*                                                                           */
/*****************************************************************************/

VOID FAR GetUnitLockStatusComplete()                                 /*@V51531*/
{
   PIORB_UNIT_STATUS pIORB = ( PIORB_UNIT_STATUS)pHeadIORB;
   NPABRB_DSKT_CONTROL pRB = (NPABRB_DSKT_CONTROL)RequestBlock;

   if ( pRB->abrbh.RC != ABRC_COMPLETEOK ) {
       pIORB->iorbh.ErrorCode = TranslateErrorCode( pRB->abrbh.RC );
       Drive[pIORB->iorbh.UnitHandle].LockState = LOCK_STATE_UNKNOWN;/*@V54106*/
   } else {
       if (pRB->LockStatus & AB_DRIVE_LOCKED) {   /* is supported and drive  */
          pIORB->UnitStatus |= US_LOCKED;         /* is locked, then set bit */
          Drive[pIORB->iorbh.UnitHandle].LockState = LOCKED;         /*@V54106*/
       } else {
          pIORB->UnitStatus &= ~US_LOCKED;           /* Drive is unlocked */
          Drive[pIORB->iorbh.UnitHandle].LockState = UNLOCKED;       /*@V54106*/
       }
   }

   TurnOffMotor( pRB->abrbh.Unit);

   IORBDone();
}

/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetChangelineState                                        */
/*                                                                           */
/*   Description : Issue the ABIOS command to Read Change Signal.            */
/*                                                                           */
/*****************************************************************************/

VOID NEAR GetChangelineState()
{
   PIORB_UNIT_STATUS pIORB = (PIORB_UNIT_STATUS)pHeadIORB;
   NPABRB_DSKT_READCHGLINE pRB = (NPABRB_DSKT_READCHGLINE)RequestBlock;

   pRB->abrbh.Function  = ABFC_DSKT_READ_CHGSIGNAL;
   pRB->abrbh.Unit      = pIORB->iorbh.UnitHandle;
   pRB->abrbh.RC        = ABRC_START;
   pRB->Reserved_1      = 0;     /* +16H reserved field  */

   CompletionRoutine = GetChangelineStateComplete;
   Retry = 0;
   Stage = ABIOS_EP_START;
   NextStage();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetChangelineStateComplete                                */
/*                                                                           */
/*   Description : This routine is called when the ABIOS Read Change         */
/*                 Line command finishes.  Updates the IORB with the         */
/*                 changeline state.                                         */
/*                                                                           */
/*****************************************************************************/

VOID FAR GetChangelineStateComplete()
{
   PIORB_UNIT_STATUS pIORB = (PIORB_UNIT_STATUS)pHeadIORB;
   NPABRB_DSKT_READCHGLINE pRB = (NPABRB_DSKT_READCHGLINE)RequestBlock;

   if ( pRB->abrbh.RC == ABRC_COMPLETEOK )
      {
         if ( pRB->ChangeLineStatus & CHANGELINE_ACTIVE )
            pIORB->UnitStatus = US_CHANGELINE_ACTIVE;
         else
            pIORB->UnitStatus = 0;
      }
   else if ( pRB->abrbh.RC == ABRC_DSKT_NOCHGSIG )
      {
         if (Drive[pIORB->iorbh.UnitHandle].Flags.MotorOn)
            pIORB->UnitStatus = 0;
         else
            pIORB->UnitStatus = US_CHANGELINE_ACTIVE;
         pIORB->iorbh.Status = 0;       /* Clear error bit */
      }
   else
      {
         pIORB->iorbh.ErrorCode = TranslateErrorCode( pRB->abrbh.RC );
      }

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

   IORBDone();
}
                                /* F26083 */
/*****************************************************************************/
/*                                                                           */
/*   Routine     : LkUnlkEjt()                                               */
/*                                                                           */
/*   Description : Issues the ABIOS command Get Media Type.                  */
/*                                                                           */
/*****************************************************************************/

VOID NEAR LkUnlkEjt()
{
   PIORB_DEVICE_CONTROL pIORB = (PIORB_DEVICE_CONTROL)pHeadIORB;
   NPABRB_DSKT_CONTROL pRB   = (NPABRB_DSKT_CONTROL)RequestBlock;

   switch ( pIORB->iorbh.CommandModifier) {
   case IOCM_LOCK_MEDIA:
      if (Drive[pHeadIORB->UnitHandle].LockState == LOCKED) {        /*@V54106*/
         IORBDone();                                                 /*@V54106*/
         return;                                                     /*@V54106*/
      }                                                              /*@V54106*/
      pRB->abrbh.Function  = ABFC_DSKT_LOCK;
      break;
   case IOCM_UNLOCK_MEDIA:
      if (Drive[pHeadIORB->UnitHandle].LockState == UNLOCKED) {      /*@V54106*/
         IORBDone();                                                 /*@V54106*/
         return;                                                     /*@V54106*/
      }                                                              /*@V54106*/
      pRB->abrbh.Function  = ABFC_DSKT_UNLOCK;
      break;
   case IOCM_EJECT_MEDIA:
      pRB->abrbh.Function  = ABFC_DSKT_EJECT;
      break;
   } /* endswitch */

   pRB->abrbh.Unit      = pIORB->iorbh.UnitHandle;
   pRB->abrbh.RC        = ABRC_START;

   CompletionRoutine = LkUnlkEjtComplete;
   Retry = 0;
   Stage = ABIOS_EP_START;
   NextStage();

}

                                 /* F26083 */
/*****************************************************************************/
/*                                                                           */
/*   Routine     : LkUnlkEjtComplete                                         */
/*                                                                           */
/*   Description : This routine is called when the ABIOS Get Media Type      */
/*                 command completes.  The IORB is updated with the          */
/*                 type of media in the drive.                               */
/*                                                                           */
/*****************************************************************************/

VOID FAR LkUnlkEjtComplete()
{
   PIORB_DEVICE_CONTROL pIORB = (PIORB_DEVICE_CONTROL)pHeadIORB;
   NPABRB_DSKT_CONTROL pRB = (NPABRB_DSKT_CONTROL)RequestBlock;

   if ( pRB->abrbh.RC != ABRC_COMPLETEOK )
        pIORB->iorbh.ErrorCode = TranslateErrorCode( pRB->abrbh.RC );
   else if (pRB->abrbh.Function == ABFC_DSKT_LOCK)                   /*@V54106*/
        Drive[pHeadIORB->UnitHandle].LockState=LOCKED;               /*@V54106*/
   else if (pRB->abrbh.Function == ABFC_DSKT_UNLOCK)                 /*@V54106*/
        Drive[pHeadIORB->UnitHandle].LockState=UNLOCKED;             /*@V54106*/

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

   IORBDone();
}



/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetMediaSense                                             */
/*                                                                           */
/*   Description : Issues the ABIOS command Get Media Type.                  */
/*                                                                           */
/*****************************************************************************/

VOID NEAR GetMediaSense()
{
   PIORB_UNIT_STATUS pIORB = (PIORB_UNIT_STATUS)pHeadIORB;
   NPABRB_DSKT_GETMEDIATYPE pRB = (NPABRB_DSKT_GETMEDIATYPE)RequestBlock;

   pRB->abrbh.Function  = ABFC_DSKT_GET_MEDIA_TYPE;
   pRB->abrbh.Unit      = pIORB->iorbh.UnitHandle;
   pRB->abrbh.RC        = ABRC_START;
   pRB->Reserved_1      = 0;     /* +16H reserved field  */

   CompletionRoutine = GetMediaSenseComplete;
   Retry = 0;
   Stage = ABIOS_EP_START;
   NextStage();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : GetMediaSenseComplete                                     */
/*                                                                           */
/*   Description : This routine is called when the ABIOS Get Media Type      */
/*                 command completes.  The IORB is updated with the          */
/*                 type of media in the drive.                               */
/*                                                                           */
/*****************************************************************************/

VOID FAR GetMediaSenseComplete()
{
   PIORB_UNIT_STATUS pIORB = (PIORB_UNIT_STATUS)pHeadIORB;
   NPABRB_DSKT_GETMEDIATYPE pRB = (NPABRB_DSKT_GETMEDIATYPE)RequestBlock;

   if ( pRB->abrbh.RC == ABRC_COMPLETEOK )
      {
         switch( pRB->MediaType )
            {
               case MEDIA1MB: pIORB->UnitStatus = US_MEDIA_720KB;   break;
               case MEDIA2MB: pIORB->UnitStatus = US_MEDIA_144MB;   break;
               case MEDIA4MB: pIORB->UnitStatus = US_MEDIA_288MB;   break;
               default:       pIORB->UnitStatus = US_MEDIA_UNKNOWN; break;
            }
      }
   else if ((pRB->abrbh.RC == ABRC_UNSUPPORTED_FUNCTION) ||  // v67100
            (pRB->abrbh.RC == ABRC_DISK_BAD_COMMAND)     ||  // v67100
            (pRB->abrbh.RC == ABRC_DSKT_NO_MEDIA_SENSE) ) {  // v67100
      pIORB->UnitStatus    = US_MEDIA_UNKNOWN;               // v67100
      pIORB->iorbh.Status &= ~IORB_ERROR;                    // v67100
   } else pIORB->iorbh.ErrorCode = TranslateErrorCode( pRB->abrbh.RC );

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

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

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : CompleteInit                                              */
/*                                                                           */
/*   Description : This IORB is issued by the loader when the booting        */
/*                 sequence has finished.  When booting OS/2 from            */
/*                 floppy, prior to this IORB, the floppy controller is      */
/*                 driven by INT 13s.  After this IORB, the floppy is        */
/*                 driven by this ADD.  This routine hooks the IRQ and       */
/*                 resets the diskette controller.                           */
/*                                                                           */
/*****************************************************************************/

VOID NEAR CompleteInit()
{
   NPABRBH pRB = (NPABRBH)RequestBlock;
   USHORT  Unit;

   /* Hook the IRQ */
   if ( DevHelp_SetIRQ( (NPFN)IntHandler, IntLevel, 1 ) )
      {
         pHeadIORB->Status |= IORB_ERROR;                      // @V88156
         pHeadIORB->ErrorCode = IOERR_CMD_SW_RESOURCE;         // @V88156
      }
   else /* Successfully hooked the IRQ */
      {
         Reset();
         while( GFlags.Resetting );
         if ( pRB->RC != ABRC_COMPLETEOK )
            {                                                  // @V88156
               pHeadIORB->Status |= IORB_ERROR;
               pHeadIORB->ErrorCode = IOERR_ADAPTER_NONSPECIFIC; // @88156
               DevHelp_UnSetIRQ( IntLevel );
            }
         else
            {
               GFlags.BootComplete = TRUE;
               for ( Unit=0; Unit<UnitCnt; Unit++ )
                  Drive[Unit].Flags.UnknownMedia = TRUE;
            }
      }

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : CmdNotSupported                                           */
/*                                                                           */
/*   Description : IORBs, which are not supported by this ADD, are           */
/*                 routed to this routine.  The appropriate error code       */
/*                 is set and the IORB is returned.                          */
/*                                                                           */
/*****************************************************************************/

VOID NEAR CmdNotSupported()
{
   pHeadIORB->Status   |= IORB_ERROR;
   pHeadIORB->ErrorCode = IOERR_CMD_NOT_SUPPORTED;

   IORBDone();
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : TranslateErrorCode                                        */
/*                                                                           */
/*   Description : This routine translates ABIOS return codes to             */
/*                 IORB return codes.                                        */
/*                                                                           */
/*****************************************************************************/

USHORT NEAR TranslateErrorCode( USHORT ABIOSRetCode )
{
   NPABRBH pRB = (NPABRBH)RequestBlock;

   /* Convert ABIOS return code to IORB return code */

  if ( (ABIOSRetCode & 0x00FF) == (ABRC_DSKT_BAD_CONTROLLER & 0x00FF) )
     ABIOSRetCode = ABRC_DSKT_BAD_CONTROLLER;

   switch( ABIOSRetCode )
      {
         case ABRC_INVALID_PARM:            return IOERR_CMD_ADD_SOFTWARE_FAILURE;
         case ABRC_DSKT_NOCHGSIG:           return IOERR_CMD_NOT_SUPPORTED;
         case ABRC_DSKT_NO_MEDIA_SENSE:     return IOERR_CMD_NOT_SUPPORTED;
         case ABRC_UNSUPPORTED_FUNCTION:    return IOERR_CMD_NOT_SUPPORTED;

         case ABRC_DSKT_INVALID_VALUE:      return IOERR_DEVICE_NONSPECIFIC;

         case ABRC_DSKT_ADDRMARK_NOTFND:    return IOERR_RBA_ADDRESSING_ERROR;
         case ABRC_DSKT_SECTOR_NOTFND:      return IOERR_RBA_ADDRESSING_ERROR;
         case ABRC_DSKT_BAD_CRC:            return IOERR_RBA_CRC_ERROR;

         case ABRC_DSKT_MEDIA_NOT_PRESENT:  return IOERR_MEDIA_NOT_PRESENT;
         case ABRC_DSKT_WRITE_PROTECT:      return IOERR_MEDIA_WRITE_PROTECT;
         case ABRC_DSKT_MEDIA_CHANGED:      return IOERR_MEDIA_CHANGED;
         case ABRC_DSKT_MEDIA_NOTSUPPORTED: return IOERR_MEDIA_NOT_SUPPORTED;
         case ABRC_DSKT_UNKNOWN_MEDIA:      return IOERR_MEDIA_NOT_SUPPORTED;
         case ABRC_DSKT_RESET_FAIL:         return IOERR_ADAPTER_NONSPECIFIC;

         /* External 5.25 drives return     controller when media not present*/
         case ABRC_DSKT_BAD_CONTROLLER:
             if (Drive[pRB->Unit].Flags.HasChangeLine)
                return IOERR_ADAPTER_NONSPECIFIC;
             else
                return IOERR_MEDIA_NOT_PRESENT;

         case ABRC_DSKT_GENERAL_ERROR:      return IOERR_ADAPTER_NONSPECIFIC;

         case ABRC_DSKT_BAD_SEEK:           return IOERR_DEVICE_NONSPECIFIC;
         case ABRC_BUSY:                    return IOERR_DEVICE_BUSY;
         case ABRC_DSKT_DMA_IN_PROGRESS:    return IOERR_DEVICE_BUSY;
         case ABRC_DSKT_DMA_OVERRUN:        return IOERR_DEVICE_OVERRUN;

         default:                           return IOERR_CMD_ADD_SOFTWARE_FAILURE;
      }
}


/*****************************************************************************/
/*                                                                           */
/*   Routine     : TurnOffMotor                                              */
/*                                                                           */
/*   Description : This routine is called by the timer when the motor        */
/*                 off delay has expired.  This routine calls ABIOS          */
/*                 to turn the diskette drive motor off.                     */
/*                                                                           */
/*****************************************************************************/

VOID FAR TurnOffMotor( USHORT Unit )
{
   NPABRB_GENERIC pRBG = (NPABRB_GENERIC)MotorOffReqBlk;

   pRBG->abrbh.Unit = Unit;
   pRBG->abrbh.RC   = ABRC_START;
   pRBG->Offset16H  = 0;          /* +16H reserved field */

   DevHelp_ABIOSCall( LID, (NPBYTE)pRBG, ABIOS_EP_START );

   Drive[Unit].Flags.MotorOn = FALSE;
}


