/*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/ibm1s506/s506iorb.c, idsk, r207, 8.005p 93/03/18";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = S506IORB.C
 *
 * DESCRIPTIVE NAME = IBM1S506.ADD - Adapter Driver for ST506/IDE DASD
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : IORB Routine/Processing
 *
 * Purpose:
 *
 *
 *
 *
 *
*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#define INCL_INITRP_ONLY

#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 "s506ext.h"
#include "s506pro.h"


/*------------------------------*/
/* ADDEntryPoint                */
/* -------------                */
/*                              */
/* ADD IORB command entry.      */
/*                              */
/*------------------------------*/


VOID FAR  _loadds ADDEntryPoint( PIORBH pIORB )
{
  PIORBH        pFirstIORB;
  PIORBH        pLastIORB;
  NPACB         npACB;
  NPUCB         npUCB;

  pFirstIORB = pIORB;

  /*------------------------------------------*/
  /* Queue IORB commands which dont address a */
  /* specific unit to ACB 0 Unit 0            */
  /*------------------------------------------*/

  if (pFirstIORB->CommandCode == IOCC_CONFIGURATION )
  {
    pFirstIORB->UnitHandle = (USHORT) (npUCB = ACBPtrs[0].npACB->UnitCB);
  }
  else
  {
    npUCB = (NPUCB) pFirstIORB->UnitHandle;
  }

  npACB = npUCB->npACB;

  if (!(pLastIORB = PreProcessIORBs( npACB, &pFirstIORB)) )        /*@V87325*/
  {                                                                /*@V87325*/
     goto ADDEntryExit;                                            /*@V87325*/
  }                                                                /*@V87325*/

  DISABLE

  /*-----------------------------------*/
  /* Add new IORBs to end of the ACB Q */
  /*-----------------------------------*/
  if (!npACB->pHeadIORB)
  {
    npACB->pHeadIORB = pFirstIORB;
  }
  else
  {
    npACB->pFootIORB->pNxtIORB = pFirstIORB;
  }

  /*--------------------------------------*/
  /* Point to the last IORB on the ACB Q  */
  /*--------------------------------------*/
  npACB->pFootIORB = pLastIORB;


  DISABLE
  if ( !(npACB->Flags & ACBF_Q_ACTIVE ) &&                         /*@V87325*/
       !(npACB->Flags & ACBF_SM_SUSPENDED ) )                      /*@V87325*/
  {
    npACB->Flags |= ACBF_Q_ACTIVE;

    ENABLE
    NextIORB(npACB);
  }

  ADDEntryExit:                                                    /*@V87325*/

  ENABLE
}

/*-----------------------------------*/
/* NextIORB                          */
/* --------                          */
/*                                   */
/* Obtains the an IORB on the ACB    */
/* IORB Queue and routes it to the   */
/* appropriate IORB handler.         */
/*-----------------------------------*/


VOID NEAR NextIORB(NPACB npACB)
{
  PIORB         pIORB;
  USHORT        CmdCode;
  USHORT        CmdModifier;
  USHORT        rc = 0;

  /*------------------------------------------*/
  /* Process the IORB Queue for this ACB      */
  /*------------------------------------------*/
  DISABLE

  if ( npACB->pSuspendIORB )                                       /*@V87325*/
  {                                                                /*@V87325*/
    if ( !npACB->pHeadIORB )                                       /*@V87325*/
    {                                                              /*@V87325*/
      npACB->CountUntilSuspend = 0;                                /*@V87325*/
    }                                                              /*@V87325*/
    else if ( npACB->CountUntilSuspend )                           /*@V87325*/
    {                                                              /*@V87325*/
      npACB->CountUntilSuspend--;                                  /*@V87325*/
    }                                                              /*@V87325*/
                                                                   /*@V87325*/
    if ( !npACB->CountUntilSuspend )                               /*@V87325*/
    {                                                              /*@V87325*/
      SuspendIORB( npACB );                                        /*@V87325*/
      rc = 1;                                                      /*@V87325*/
    }                                                              /*@V87325*/
  }                                                                /*@V87325*/

  while ( (pIORB = npACB->pHeadIORB) && !rc  )
  {

    if ( npACB->pIORB )                                            /*@V87325*/
    {                                                              /*@V87325*/
       if ( !( npACB->pIORB->Status & IORB_DONE ) )                /*@V87325*/
       {                                                           /*@V87325*/
          _asm INT 3                                               /*@V87325*/
       }                                                           /*@V87325*/
    }                                                              /*@V87325*/

    npACB->pIORB = pIORB;

    if ( !(npACB->pHeadIORB = pIORB->pNxtIORB) )
    {
      npACB->pFootIORB = 0;
    }
    ENABLE

    CmdCode     = pIORB->CommandCode;
    CmdModifier = pIORB->CommandModifier;

    npACB->npUCB = (NPUCB) npACB->pIORB->UnitHandle;

    /*------------------------------------------------*/
    /* Check that unit is allocated for IORB commands */
    /* which require an allocated unit                */
    /*------------------------------------------------*/
    if ( !(CmdCode == IOCC_CONFIGURATION) &&
         !(CmdCode == IOCC_UNIT_CONTROL && CmdModifier == IOCM_ALLOCATE_UNIT)&&
         !(CmdCode == IOCC_RESOURCE &&                             /*@V87325*/
                  CmdModifier == IOCM_REPORT_RESOURCES ) )         /*@V87325*/
    {
      if ( !(npACB->npUCB->Flags & UCBF_ALLOCATED) && !InitActive )
      {
        /* Unit not allocated */
        pIORB->Status   |= IORB_ERROR;
        pIORB->ErrorCode = IOERR_UNIT_NOT_ALLOCATED;

        IORBDone( npACB );
        return;
      }
    }

    /*-------------------------------------------*/
    /* Route the iorb to the appropriate handler */
    /*-------------------------------------------*/

    switch ( CmdCode )
    {
      case IOCC_CONFIGURATION:
        switch( CmdModifier )
        {
          case IOCM_GET_DEVICE_TABLE:    break;                     /*@V108783*/
          case IOCM_COMPLETE_INIT:       CompleteInit(npACB);    break;
          default:                       CmdNotSupported(npACB); break;
        }
      break;

      case IOCC_UNIT_CONTROL:
        switch( CmdModifier )
        {
          case IOCM_ALLOCATE_UNIT:       AllocateUnit(npACB);    break;
          case IOCM_DEALLOCATE_UNIT:     DeallocateUnit(npACB);  break;
          case IOCM_CHANGE_UNITINFO:     ChangeUnitInfo(npACB);  break;
          default:                       CmdNotSupported(npACB); break;
        }
      break;


      case IOCC_GEOMETRY:
        switch( CmdModifier )
        {
          case IOCM_GET_MEDIA_GEOMETRY:
          case IOCM_GET_DEVICE_GEOMETRY: GetGeometry(npACB);     break;
          default:                       CmdNotSupported(npACB); break;
        }
      break;


      case IOCC_EXECUTE_IO:
        switch( CmdModifier )
        {
          case IOCM_NO_OPERATION:
          case IOCM_READ:
          case IOCM_READ_VERIFY:
          case IOCM_WRITE:
          case IOCM_WRITE_VERIFY:        rc = 1;
                                         ExecuteIO(npACB);       break;
          default:                       CmdNotSupported(npACB); break;
        }
      break;


      case IOCC_UNIT_STATUS:
        switch( CmdModifier )
        {
          case IOCM_GET_UNIT_STATUS:     GetUnitStatus(npACB);      break;
          default:                       CmdNotSupported(npACB);    break;
        }
      break;

      case IOCC_RESOURCE :                                         /*@V87325*/
        switch( CmdModifier )                                      /*@V87325*/
        {                                                          /*@V87325*/
          case IOCM_REPORT_RESOURCES:  GetUnitResources(npACB);    /*@V87325*/
                                       break;                      /*@V87325*/
          default:                     CmdNotSupported(npACB);     /*@V87325*/
                                       break;                      /*@V87325*/
        }                                                          /*@V87325*/
      break;                                                       /*@V87325*/

      default:
        CmdNotSupported(npACB);
      break;
    }

    DISABLE
  }


  if ( !rc && !pIORB )
  {
    npACB->Flags &= ~ACBF_Q_ACTIVE;
  }

  return;

}


/*------------------------------------------*/
/* GetDeviceTable                           */
/* --------------                           */
/*                                          */
/* Handles an IOCM_GET_DEVICETABLE request  */
/*------------------------------------------*/

VOID NEAR GetDeviceTable(NPACB npACB, PIORB pNewIORB)               /*@V108783*/
{
  PIORB_CONFIGURATION pIORB = (PIORB_CONFIGURATION)pNewIORB;        /*@V108783*/

  USHORT        LengthNeeded;
  USHORT        i;
  USHORT        j;
  PADAPTERINFO  pADPT;
  PUNITINFO     pUI, pUI2;
  PDEVICETABLE  pDT;
  NPACB         npACBP;

  LengthNeeded = sizeof(DEVICETABLE) + cAdapters * sizeof(ADAPTERINFO) +
                 (cUnits * sizeof(UNITINFO));

  if ( pIORB->DeviceTableLen < LengthNeeded )
  {
    pIORB->iorbh.Status   |= IORB_ERROR;
    pIORB->iorbh.ErrorCode = IOERR_CMD_SYNTAX;
    IORBDone(npACB);
    return;
  }

  pDT = pIORB->pDeviceTable;

  pDT->ADDLevelMajor = ADD_LEVEL_MAJOR;
  pDT->ADDLevelMinor = ADD_LEVEL_MINOR;
  pDT->ADDHandle     = ADDHandle;
  pDT->TotalAdapters = cAdapters;

  pADPT = (PADAPTERINFO) (((PBYTE)(pDT+1)) +
                                 (cAdapters-1) * sizeof(NPADAPTERINFO));

  for ( i = 0; i < cAdapters; i++ )
  {
    pDT->pAdapter[i]  = (NPADAPTERINFO) pADPT;

    memcpy( pADPT->AdapterName, AdapterName, sizeof(AdapterName) );

    npACBP = ACBPtrs[i].npACB;

    pADPT->AdapterUnits         = npACBP->cUnits;
    pADPT->AdapterDevBus        = AI_DEVBUS_ST506 | AI_DEVBUS_16BIT;
    pADPT->AdapterIOAccess      = AI_IOACCESS_PIO;
    pADPT->AdapterHostBus       = AI_HOSTBUS_ISA | AI_BUSWIDTH_16BIT;
    pADPT->AdapterSCSITargetID  = 0;
    pADPT->AdapterSCSILUN       = 0;
    pADPT->AdapterFlags         = AF_16M | AF_HW_SCATGAT;
    pADPT->MaxHWSGList          = 0;
    pADPT->MaxCDBTransferLength = 0L;

    for ( j=0; j < npACBP->cUnits; j++ )
    {
      pUI = &pADPT->UnitInfo[j];

      if ( !(pUI2=npACBP->UnitCB[j].pUnitInfo) ) /* If unit info was not changed */
      {
        setmem( (PBYTE) pUI, sizeof(UNITINFO), 0 );

        pUI->AdapterIndex     = i;
        pUI->UnitIndex        = j;
        pUI->UnitHandle       = (USHORT) &npACBP->UnitCB[j];
        pUI->FilterADDHandle  = 0;

        if (npACBP->UnitCB[j].Flags & UCBF_ATAPI_DEVICE)           /*@V87325*/
          pUI->UnitType         = UIB_TYPE_ATAPI;                  /*@V87325*/
        else                                                       /*@V87325*/
          pUI->UnitType         = UIB_TYPE_DISK;

        pUI->QueuingCount     = 3;
        pUI->UnitFlags        = UF_NOSCSI_SUPT;

        if ( npACBP->UnitCB[j].Flags & UCBF_NODASDSUPPORT )
        {
          pUI->UnitFlags |= UF_NODASD_SUPT;
        }
      }
      else  /* Pass back the changed unit info */
      {
        *pUI = *pUI2;
      }

    }
    pADPT = (PADAPTERINFO) (pUI+1);
  }

  pIORB->iorbh.Status |= IORB_DONE;                                 /*@V108783*/

  if ( pIORB->iorbh.RequestControl & IORB_ASYNC_POST )              /*@V108783*/
  {                                                                 /*@V108783*/
    pIORB->iorbh.NotifyAddress( pNewIORB );                         /*@V108783*/
  }                                                                 /*@V108783*/

}


/*------------------------------------------*/
/* CompleteInit                             */
/* ------------                             */
/*                                          */
/*                                          */
/*------------------------------------------*/

VOID NEAR CompleteInit(NPACB npACB)
{
  USHORT        i;

  for (i=0; i < cAdapters; i++ )
  {
    if ( ACBPtrs[i].npACB->Flags & ACBF_PS2IDEPORT )
    {
      SetupPS2IDEPort( 1 );
      break;
    }
  }

  BIOSActive = 0;

  IORBDone(npACB);
}


/*------------------------------------------*/
/* AllocateUnit                             */
/* ------------                             */
/*                                          */
/* Mark a drive as allocated                */
/*                                          */
/*------------------------------------------*/

VOID NEAR AllocateUnit(NPACB npACB)
{
  PIORB         pIORB;
  NPUCB         npUCB;

  pIORB = npACB->pIORB;
  npUCB = (NPUCB) pIORB->UnitHandle;

  if ( npUCB->Flags & UCBF_ALLOCATED )
  {
    pIORB->Status   |= IORB_ERROR;
    pIORB->ErrorCode = IOERR_UNIT_ALLOCATED;
  }
  else
  {
    npUCB->Flags |= UCBF_ALLOCATED;
  }

  IORBDone(npACB);
}

/*------------------------------------------*/
/* DeallocateUnit                           */
/* --------------                           */
/*                                          */
/* Deallocate a previously allocated drive  */
/*                                          */
/*------------------------------------------*/


VOID NEAR DeallocateUnit(NPACB npACB)
{
  PIORB         pIORB;
  NPUCB         npUCB;

  pIORB = npACB->pIORB;
  npUCB = (NPUCB) pIORB->UnitHandle;

  if ( !(npUCB->Flags & UCBF_ALLOCATED) )
  {
    /* Unit not allocated */
    pIORB->Status    |= IORB_ERROR;
    pIORB->ErrorCode  = IOERR_UNIT_NOT_ALLOCATED;
  }
  else
  {
    npUCB->Flags &= ~UCBF_ALLOCATED;
  }

  IORBDone(npACB);
}


/*------------------------------------------*/
/* ChangeUnitInfo                           */
/* --------------                           */
/*                                          */
/* Store pointer to new UNITINFO structure  */
/*                                          */
/*------------------------------------------*/

VOID NEAR ChangeUnitInfo( NPACB npACB )
{

  PIORB         pIORB;
  NPUCB         npUCB;

  pIORB = npACB->pIORB;
  npUCB = (NPUCB) pIORB->UnitHandle;


  if ( !npUCB->Flags & UCBF_ALLOCATED )
  {
    /* Unit not allocated */
    pIORB->Status   |= IORB_ERROR;
    pIORB->ErrorCode = IOERR_UNIT_NOT_ALLOCATED;

    IORBDone( npACB );
    return;
  }

  /* Save the Unit Info pointer */
  npUCB->pUnitInfo = ((PIORB_UNIT_CONTROL) pIORB)->pUnitInfo;

  IORBDone( npACB );
}

/*--------------------------------------------------------------*/
/* GetGeometry                                                  */
/* -----------                                                  */
/*                                                              */
/* Handles IOCM_GET_DEVICE_GEOMETRY and IOCM_GET_MEDIA_GEOMETRY */
/* which both return the same geometry for Fixed Media Devices. */
/*                                                              */
/* The Logical (INT 13) geometry is always returned. However,   */
/* cylinder and sector counts ARE NOT bounded by INT 13         */
/* limits.                                                      */
/*                                                              */
/*--------------------------------------------------------------*/

VOID NEAR GetGeometry(NPACB npACB)
{

  NPGEOMETRY npGeometry;

  PIORB         pIORB;
  NPUCB         npUCB;

  pIORB = npACB->pIORB;
  npUCB = (NPUCB) pIORB->UnitHandle;

  *((PIORB_GEOMETRY) pIORB)->pGeometry = npUCB->LogGeom;

  IORBDone(npACB);
}


/*--------------------------------------------------------------*/
/* Execute IO                                                   */
/* ----------                                                   */
/*                                                              */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/


VOID NEAR ExecuteIO(NPACB npACB)
{
  npACB->State = ACBS_START;

  if ( !(npACB->Flags & ACBF_SM_ACTIVE) )
  {
    npACB->Flags |= ACBF_SM_ACTIVE;
    FixedExecute( npACB );
  }
}



/*--------------------------------------------------------------*/
/* GetUnitStatus                                                */
/* -------------                                                */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/


VOID NEAR GetUnitStatus( NPACB npACB )
{
  PIORB_UNIT_STATUS pIORB;
  NPUCB         npUCB;

  pIORB = (PIORB_UNIT_STATUS) npACB->pIORB;
  npUCB = (NPUCB) pIORB->iorbh.UnitHandle;

   pIORB->UnitStatus |= US_POWER
                           | ((npUCB->Flags & UCBF_READY) ? US_READY : 0);

   IORBDone( npACB );
}

/*--------------------------------------------------------------*/ /*@V87325*/
/* GetQueueStatus                                               */ /*@VVVVVV*/
/* --------------                                               */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/


VOID NEAR GetQueueStatus( NPACB npACB, PIORB pIORB )
{
  PIORB_DEVICE_CONTROL pDCIORB;
  NPUCB         npUCB;

  pDCIORB = (PIORB_DEVICE_CONTROL) pIORB;
  npUCB = (NPUCB) pDCIORB->iorbh.UnitHandle;

  pDCIORB->QueueStatus = (ULONG) npACB->pHeadIORB;

  pIORB->Status |= IORB_DONE;

  if ( pIORB->RequestControl & IORB_ASYNC_POST )
  {
     pIORB->NotifyAddress( pIORB );
  }
}

/*--------------------------------------------------------------*/
/* GetUnitResources                                             */
/* ----------------                                             */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/


VOID NEAR GetUnitResources( NPACB npACB )
{
  PIORB_RESOURCE    pIORB   = (PIORB_RESOURCE) npACB->pIORB;
  PRESOURCE_ENTRY   pREHead = pIORB->pResourceEntry;
  PBYTE             pRE;
  USHORT            ReqLength;

  PIRQ_ENTRY        pIRQ;
  PPORT_ENTRY       pPort;

  ReqLength = sizeof(RESOURCE_ENTRY) +
              sizeof(IRQ_ENTRY) + (2 * sizeof(PORT_ENTRY));

  if ( ReqLength <= pIORB->ResourceEntryLen ) /* Valid request */
  {
     setmem( (PBYTE) pREHead, 0, pIORB->ResourceEntryLen );

     /* The resource entry structure contains pointers to variable length
        arrays.  Because of this, the called driver must manage the structure
        and its contents. This is accomplished by using the pRE pointer.
        It initially points to the first non-used byte in the Resource Entry
        buffer.  Each time a resource entry is added, the it is incremented
        by the size of the latest entry.
     */

     /*point directly after resource entry structure */
     pRE = (PBYTE) (pREHead+1);

     /* Update Resource Entry structure  */
     pREHead->cIRQ_Entries = 1;
     pREHead->npIRQ_Entry = (NPIRQ_ENTRY)pRE;

     pIRQ = (PIRQ_ENTRY) pRE;
     pRE  = (PBYTE)      (pIRQ+1);           /* point past current structure */

     pIRQ->IRQ_Flags = RE_ADAPTER_RESOURCE | IRQ_RISING_EDGE_TRIGGERED;
     pIRQ->IRQ_Value = npACB->IntLevel;

     /* Update Resource Entry structure  */
     pREHead->cPort_Entries = 2;
     pREHead->npPort_Entry = (NPPORT_ENTRY)pRE;

     pPort = (PPORT_ENTRY) pRE;
     pRE   = (PBYTE)       (pPort+1);        /* point past current structure */

     pPort->Port_Flags = RE_ADAPTER_RESOURCE;
     pPort->StartPort  = npACB->IOPorts[0];
     pPort->cPorts     = 8;

     pPort = (PPORT_ENTRY) pRE;
     pRE   = (PBYTE)       (pPort+1);        /* point past current structure */

     pPort->Port_Flags = RE_ADAPTER_RESOURCE;
     pPort->StartPort  = npACB->IOPorts[8];
     pPort->cPorts     = 2;

     pREHead->Max_Resource_Entry = RE_PORT; /* Establish last table entry
                                           position                          */

  }
  else
  {
     pIORB->iorbh.Status   |= IORB_ERROR;
     pIORB->iorbh.ErrorCode = IOERR_CMD_SYNTAX;
  }

  IORBDone( npACB );
}                                                                  /*AAAAAAA*/
                                                                   /*@V87325*/


/*--------------------------------------------------------------*/
/* CmdNotSupported                                              */
/* ---------------                                              */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/

VOID NEAR CmdNotSupported(NPACB npACB)
{
  PIORB         pIORB;

  pIORB = npACB->pIORB;

  pIORB->Status   |= IORB_ERROR;
  pIORB->ErrorCode = IOERR_CMD_NOT_SUPPORTED;

  IORBDone(npACB);
}

/*--------------------------------------------------------------*/
/* IORBDone                                                     */
/* --------                                                     */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/

VOID NEAR IORBDone(NPACB npACB)
{
  PIORB        pIORB;

  pIORB = npACB->pIORB;

  npACB->pIORB = 0;                                                /*@V87325*/

  pIORB->Status |= IORB_DONE;

  if ( pIORB->RequestControl & IORB_ASYNC_POST )
  {
    pIORB->NotifyAddress( pIORB );
  }
}


