/*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.      */
/*                                                                           */
/*****************************************************************************/
/* SCCSID = %W% %E% */
/****************************************************************************
 *                                                                          *
 *                           OCO Source Materials                           *
 *                                                                          *
 *                                                                          *
 ****************************************************************************/
/**@internal s506i13.c
 *  Initialize and control a mini-VDM thread to execute int-13 BIOS calls
 *  for IBM1S506.ADD.  The interface usable only during device driver
 *  initialization.
 * @version 1.00
 * @context 16-bit, Ring-0, device driver initialization.
 * @history
 *  02/20/98 192176 Vince Rapp      New file.  Origins of the int 13 interface
 *   for device drivers was from IBMINT13.I13 driver.
 *  10/21/98 203676 Vince Rapp      Made int13 worker thread non-interruptable
 *   to prevent waking it up after initialization is complete and trapping the
 *   system.  Still need to get the DestroyInt13VDM to work so that resources
 *   are conserved but this fixes the immediate problem.
 *  02/21/99 220858 Vince Rapp      Added timeout for int13 requests.
 */

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#define INCL_DOSINFOSEG
#define INCL_NO_SCB
#define INCL_INITRP_ONLY
#define INCL_DOSERRORS
#include <os2.h>
#include <dskinit.h>

#include <iorb.h>
#include <reqpkt.h>
#include <dhcalls.h>
#include <addcalls.h>

#include "s506cons.h"
#include "s506type.h"
#include "s506pro.h"
#include "s506ext.h"


/**@internal VDMInt13Create()
 *  Set up the DiskDD to INT 13 VDM communications area and pass it to
 * DevHelp_CreateInt13VDM.  Verify the VDM was created successfully.
 *
 * @return
 *  FALSE - the mini-VDM was successfully created.
 * @return
 *  TRUE - creation of the mini-VDM failed.
 */
BOOL NEAR VDMInt13Create( VOID )
{
   USHORT rc = 0;

   // Setup Service routine addresses
   // Setup VDM<->diskDD communication area
   _asm
   {
      mov  WORD PTR VDMInt13.Int13Functions[INT13_FCN_DISKDDCALLOUT][0], Offset StubVDMInt13CallBack
      mov  WORD PTR VDMInt13.Int13Functions[INT13_FCN_DISKDDCALLOUT][2], 0
      mov  WORD PTR VDMInt13.Int13Functions[INT13_FCN_DISKDDCALLOUT][4], cs
      mov  WORD PTR VDMInt13.Int13Functions[INT13_FCN_DISKDDCALLOUT][6], 0
   }


   VDMInt13.Int13State    = VDMINT13_IDLE;
   VDMInt13.Int13PriClass = 0x03;
   VDMInt13.Int13PriLevel = 0x1F;
   VDMInt13.Int13CreateRc = 0;

   if( !(rc = DevHelp_CreateInt13VDM( (char far *)&VDMInt13 )) )
   {
      DevHelp_ProcBlock( (ULONG)VDMDskBlkID,       // Wait for VDM to run
                         (ULONG)-1,
                         (USHORT)0          );
   }

   if( !rc && !VDMInt13.Int13CreateRc )
   {
      return( FALSE );
   }
   else
   {
      return( TRUE );
   }
}


/**@internal VDMInt13Destroy()
 *  Destroy the mini-VMD used to execute int-13 BIOS calls for the
 * DiskDD.
 *
 * @return
 *  none.
 *
 * @notes
 *  This function is not currently implemented as the OS/2 kernel is not
 * exporting the int 13 VDM destroy function in the dev_help list.  Once
 * this export is supported then a dev_help function should be added and
 * called from this routine to remove the mini-VDM thread.  The min-VDM
 * does allocate a 64KB I/O buffer as well as the kernel data structures
 * required to support the mini-VMD thread.
 */
VOID NEAR VDMInt13Destroy( VOID )
{
   return;
}


//
//   Int13CheckEnhancedSupportPresent()
//
// Return FALSE if this drive supports the enhanced BIOS extensions
// for fixed disk drives.  If not then return TRUE.
//
BOOL NEAR Int13CheckExtensionsPresent( NPUTBL npUT )
{
   // AH = function number.
   VDMInt13.Int13Regs.R_EAX = BIOS_GET_ENHANCED_EXTENSIONS << 8;
   // BX = input signature.
   VDMInt13.Int13Regs.R_EBX = BIOS_EXTENSIONS_SIGNATURE_IN;
   // DL = BIOS driver number.
   VDMInt13.Int13Regs.R_EDX = npUT->DriveId;

   // ES = F000h (workaround for Phoenix BIOS bug) - d205595
   VDMInt13.Int13Regs.R_ES = 0xF000;

   // Do the Int 13 function.
   if( !Int13DoRequest() )
   {
      if( !(VDMInt13.Int13Regs.R_EFLAGS & EFLAGS_CF) &&
           (VDMInt13.Int13Regs.R_EBX & 0x0000ffffl) == BIOS_EXTENSIONS_SIGNATURE_OUT )
      {
         npUT->EBIOSVersion      = (VDMInt13.Int13Regs.R_EAX >> 8) &
                                   0x000000ffl;
         npUT->EBIOSCapabilities = VDMInt13.Int13Regs.R_ECX &
                                   0x0000ffffl;

         if( npUT->EBIOSVersion >= BIOS_EXTENSIONS_MIN_VERSION &&
             ((npUT->EBIOSCapabilities & BIOS_EXTENSIONS_FIXED_DISK) ||
              (npUT->EBIOSCapabilities & BIOS_EXTENSIONS_LOCK_EJECT) ||
              (npUT->EBIOSCapabilities & BIOS_EXTENSIONS_ENHANCED)) )
         {
            // Sufficient enhanced BIOS support is present for this drive.
            return( FALSE );
         }
      }
#ifdef DEBUG
      else
      {
         UCHAR BIOSError = (VDMInt13.Int13Regs.R_EAX & 0x0000ff00l) >> 8;
         _asm{ int 3 }
      }
#endif
   }

   return( TRUE );
}


//
//   Int13Fun08GetLogicalGeom()
//
// Get the logical geometry from the legacy BIOS interface.
//
BOOL NEAR Int13Fun08GetLogicalGeom( NPUTBL npUT )
{
   // AH = function number.
   VDMInt13.Int13Regs.R_EAX = BIOS_GET_LEGACY_DRIVE_PARAM << 8;
   // DL = BIOS driver number.
   VDMInt13.Int13Regs.R_EDX = npUT->DriveId;

   // Do the Int 13 function.
   if( !Int13DoRequest() )
   {
      if( !(VDMInt13.Int13Regs.R_EFLAGS & EFLAGS_CF) )
      {
         npUT->I13LogGeom.TotalCylinders  = (((VDMInt13.Int13Regs.R_ECX & 0x0000ff00l) >> 8) |
                                             ((VDMInt13.Int13Regs.R_ECX & 0x000000c0l) << 2)) + 1;
         npUT->I13LogGeom.NumHeads        = ((VDMInt13.Int13Regs.R_EDX & 0x0000ff00l) >> 8) + 1;
         npUT->I13LogGeom.SectorsPerTrack = (VDMInt13.Int13Regs.R_ECX & 0x0000003fl);

         if( !LogGeomValidate( npUT->I13LogGeom )  )
         {
            return( FALSE );
         }
      }
#ifdef DEBUG
      else
      {
         UCHAR BIOSError = (VDMInt13.Int13Regs.R_EAX & 0x0000ff00l) >> 8;
         _asm{ int 3 }
      }
#endif
   }

   return( TRUE );
}


/*--------------------------------------------------------------------*
 *                                                                    *
 *  Subroutine Name: Int13DoRequest()                                 *
 *                                                                    *
 *  This routine unblocks the INT 13 worker thread and blocks until   *
 *  the thread completes its request.                                 *
 *                                                                    *
 *--------------------------------------------------------------------*/
BOOL NEAR Int13DoRequest( VOID )
{
   USHORT  rc = FALSE;   // assume success
   USHORT  AwakeCount;

   VDMInt13Active = TRUE;
   VDMInt13.Int13State = VDMINT13_START;
                                               // Ready VDM Worker thread
   if( !(rc = DevHelp_ProcRun( (ULONG)VDMInt13BlkID,
                               (PUSHORT)&AwakeCount)) )
   {
      // Start: 220858
      // Added a timeout on all INT13 requests, this might prevent 
      // some hangs during boot for ill-behaved BIOS.
      while( VDMInt13.Int13State != VDMINT13_COMPLETE )
      {
         rc = DevHelp_ProcBlock( (ULONG)VDMDskBlkID,   // Block this device driver thread
                                 (ULONG)1000,          // Timeout in 1 second
                                 (USHORT)0 );          // Wait is interruptable
         DISABLE
         if( rc == WAIT_TIMED_OUT )
         {
            rc = TRUE;   // return failure
            break;
         }
      }
      // End: 220858
   }

   VDMInt13Active = FALSE;

  return( rc );
}


/*--------------------------------------------------------------------*
 *                                                                    *
 *  Subroutine Name: VMDInt13CallBack()                               *
 *                                                                    *
 *  This routine gets called on the INT 13 VDM thread when the        *
 *  VDM has completed a request. The thread which initiated           *
 *  the request is unblocked and the INT 13 VDM thread is             *
 *  captured by this routine.                                         *
 *                                                                    *
 *--------------------------------------------------------------------*/


VOID NEAR _loadds VDMInt13CallBack( VOID )
{
   USHORT rc;
   USHORT AwakeCount;

   DISABLE;
   rc = 0;

   // Wake up DiskDD Thread
   if( !(rc = DevHelp_ProcRun( (ULONG)VDMDskBlkID,
                               (PUSHORT)&AwakeCount )) )
   {
      // Block this (Int13 VDM) thread.  Wait is NOT interruptable.
      DevHelp_ProcBlock( (ULONG)VDMInt13BlkID,
                         (ULONG)-1,
                         (USHORT)1 );                               // 207661
   }
   ENABLE;

   return;
}

