/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "%w% %e%";*/
/**************************************************************************
 *
 * SOURCE FILE NAME = S506OSM2.C
 *
 * DESCRIPTIVE NAME = Outer State Machine - Module 2
 *
 *
 *
 * VERSION = V2.1
 *
 * DATE = 94/06/04
 *
 * DESCRIPTION :
 *
 * Purpose:  Functions are implemented this OSM Module:
 *
 *               S506OSM2.C - IORB processing for
 *                              - IOCC_DEVICE_CONTROL
 *                                - IOCM_SUSPEND
 *                                - IOCM_RESUME
 *                            ACB Activation/Deactivation
 *                            DMA Buffer Allocate/Deallocate
 *                            Motor Idle Watchdog routine
 *
 *
 *
*/
#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"
#include "dos.h"

#include "dskinit.h"

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

#define INCL_INITRP_ONLY
#include "reqpkt.h"

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

/*------------------------------------------------------------------------*/
/*  SUSPEND FUNCTIONS                                                     */
/*  -----------------                                                     */
/*------------------------------------------------------------------------*/

/*------------------------------------------------*/
/* SuspendIORB                                    */
/*                                                */
/* This routine handles suspend IORBs. The IORB   */
/* is queued on the HWResource structure for      */
/* the UNIT it is directed to.                    */
/*                                                */
/* If the ACB is not processing IORBs, then       */
/* suspend processing may be initited by this     */
/* routine.                                       */
/*                                                */
/*------------------------------------------------*/

USHORT NEAR SuspendIORBReq( NPACB npACB, PIORB pNewIORB )
{
   USHORT        InitiateSuspend = 0;
   USHORT        DCFlags;
   PIORB         pSuspendIORBQ;


   npACB->Flags      |= ACBF_SM_SUSPENDED;
   pNewIORB->pNxtIORB = 0;
   DCFlags            = ((PIORB_DEVICE_CONTROL) pNewIORB)->Flags;

   DISABLE

   /*------------------------------------------------------------------*/
   /* If a suspend is on the suspendqueue, put imediatesuspends at the */
   /* head of the queue and defered at the end.                        */
   /*------------------------------------------------------------------*/

   if ( npACB->pSuspendIORB )
   {
      if ( DCFlags & DC_SUSPEND_IMMEDIATE )
      {
         pNewIORB->pNxtIORB   = npACB->pSuspendIORB;
         npACB->pSuspendIORB = pNewIORB;
      }
      else
      {
         pSuspendIORBQ = npACB->pSuspendIORB;
         while ( pSuspendIORBQ->pNxtIORB )
         {
            pSuspendIORBQ = pSuspendIORBQ->pNxtIORB;
         }
         pSuspendIORBQ->pNxtIORB = pNewIORB;
      }
   }

   /*-------------------------------*/
   /*  Put new suspendiorb on Queue */
   /*-------------------------------*/

   else
   {
      if ( DCFlags & DC_SUSPEND_IMMEDIATE )
      {
         npACB->pSuspendIORB = pNewIORB;
         npACB->CountUntilSuspend = IMMEDIATE_COUNT;
      }
      else
      {
         npACB->pSuspendIORB      = pNewIORB;
         npACB->CountUntilSuspend = DEFERRED_COUNT;
      }
   }

   if ( !( npACB->Flags & ACBF_Q_ACTIVE ) )
   {
      SuspendIORB( npACB );
   }
   ENABLE
}

/*-------------------------------*/
/*                               */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/
USHORT NEAR SuspendIORB( NPACB npACB )
{

  PIORB         pIORB;
  USHORT        Flags;

  DISABLE

  if ( npACB->pIORB )
  {
     _asm INT 3
  }
  pIORB = npACB->pSuspendIORB;
  Flags = ((PIORB_DEVICE_CONTROL) pIORB)->Flags;
  npACB->pSuspendIORB = pIORB->pNxtIORB;

  if ( npACB->pSuspendIORB )
  {
    Flags = ((PIORB_DEVICE_CONTROL) npACB->pSuspendIORB)->Flags;
    if ( Flags & DC_SUSPEND_IMMEDIATE )
       npACB->CountUntilSuspend = IMMEDIATE_COUNT;
    else
       npACB->CountUntilSuspend = DEFERRED_COUNT;
  }

  if (( Flags & DC_SUSPEND_IRQADDR_VALID ) &&                      /*@V93531*/
      ( npACB->ResourceFlags & ACBRF_CURR_OWNER ))                 /*@V93531*/
  {                                                                /*@V93531*/
    npACB->ResourceFlags &= ~ACBRF_CURR_OWNER;                     /*@V93531*/
    npACB->SuspendIRQaddr =                                        /*@V93531*/
               ((PIORB_DEVICE_CONTROL) pIORB)->IRQHandlerAddress;  /*@V93531*/
  }                                                                /*@V93531*/
  else                                                             /*@V93531*/
  {
    pIORB->Status |= IORB_ERROR;
    pIORB->ErrorCode = IOERR_CMD_SYNTAX;
  }

  ENABLE

  pIORB->Status |= IORB_DONE;

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

/*-------------------------------*/
/*                               */
/*                               */
/*                               */
/*                               */
/*-------------------------------*/
USHORT NEAR ResumeIORBReq ( NPACB npACB, PIORB pIORB )
{
  npACB->pResumeIORB = pIORB;
  ResumeIORB( npACB );
}

VOID FAR ResumeIORB( NPACB npACB )
{
  PIORB    pIORB;

  pIORB = npACB->pResumeIORB;
  npACB->pResumeIORB = 0;

  if (!(npACB->ResourceFlags & ACBRF_CURR_OWNER) &&                 /*@V93531*/
       (npACB->SuspendIRQaddr))                                     /*@V93531*/
  {                                                                 /*@V93531*/
    npACB->ResourceFlags |= ACBRF_CURR_OWNER;                       /*@V93531*/
    npACB->SuspendIRQaddr=0L;                                       /*@V93531*/
  }                                                                 /*@V93531*/
  else                                                              /*@V93531*/
  {
    pIORB->Status |= IORB_ERROR;
    pIORB->ErrorCode = IOERR_CMD_SYNTAX;
  }

  DISABLE
  npACB->Flags &= ~( ACBF_SM_SUSPENDED | ACBF_SM_ACTIVE );
  npACB->Flags |= ACBF_Q_ACTIVE;

  NextIORB( npACB );
  ENABLE

  pIORB->Status |= IORB_DONE;

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

}


/*--------------------------------------------------*/
/* ActivateACB                                      */
/*                                                  */
/* If the IRQ level is not hooked, hook it.         */
/*                                                  */
/*--------------------------------------------------*/

USHORT FAR ActivateACB( NPACB npACB )
{
  ULONG         PageListCount;
  USHORT        rc = 0;

  DISABLE
  if ( !(npACB->ResourceFlags & ACBRF_CURR_OWNER) )
  {
    npACB->ResourceFlags |= ACBRF_CURR_OWNER;
    rc = DevHelp_SetIRQ( (NPFN)   npACB->IRQEntry,
                         (USHORT) npACB->IntLevel,
                         (USHORT) 0 );

  }

  ENABLE

  return ( rc );
}

/*---------------------------------------------------------*/
/* DeactivateACB                                           */
/*                                                         */
/*                                                         */
/*---------------------------------------------------------*/

VOID FAR DeactivateACB( NPACB npACB )
{

  if ( npACB->ResourceFlags & ACBRF_CURR_OWNER )
  {
    npACB->ResourceFlags &= ~ACBRF_CURR_OWNER;
    DevHelp_UnSetIRQ( (USHORT) npACB->IntLevel );
  }

}

/*-------------------------------------------------------*/
/* PreProcessIORBs                                       */
/*                                                       */
/* This filters incomming IORBs as they are passed       */
/* to the ADD driver.                                    */
/*                                                       */
/* Certain IORBs such as SUSPEND/RESUME are removed      */
/* from the IORB stream and routed directly to their     */
/* handling routines.                                    */
/*                                                       */
/* The remaining IORBs are placed on the device queue.   */
/*                                                       */
/*-------------------------------------------------------*/

PIORB NEAR PreProcessIORBs( NPACB npACB, PPIORB ppFirstIORB )
{
  PIORB         pIORB, pIORBPrev, pIORBNext;

  pIORB     = *ppFirstIORB;
  pIORBPrev = 0;

  do
  {
    pIORBNext = (pIORB->RequestControl & IORB_CHAIN) ? pIORB->pNxtIORB : 0;

    switch ( pIORB->CommandCode )
    {
      case IOCC_CONFIGURATION:                                      /*@V108783*/
        if ( pIORB->CommandModifier == IOCM_GET_DEVICE_TABLE )      /*@V108783*/
        {                                                           /*@V108783*/
            pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );/*@V108783*/
            GetDeviceTable(npACB, pIORB);                           /*@V108783*/
            continue;                                               /*@V108783*/
        }                                                           /*@V108783*/
        break;                                                      /*@V108783*/

      case IOCC_DEVICE_CONTROL:
        switch ( pIORB->CommandModifier )
        {
          case IOCM_SUSPEND:
            pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
            SuspendIORBReq( npACB, pIORB );

            continue;

          case IOCM_RESUME:
            pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
            ResumeIORBReq( npACB, pIORB );

            continue;

          case IOCM_GET_QUEUE_STATUS:
            pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
            GetQueueStatus( npACB, pIORB );

            continue;
        }
        break;
    }

    pIORBPrev = pIORB;
  }
  while ( pIORB = pIORBNext );

  if ( pIORBPrev )
  {
    pIORBPrev->pNxtIORB = 0;
  }

  return ( pIORBPrev );
}

/*------------------------------------*/
/* RemoveIORB                         */
/*                                    */
/* This routine removes an IOBR       */
/* we decided not to place on the     */
/* Device queue, and repairs the IORB */
/* chain if necessary.                */
/*                                    */
/*------------------------------------*/

VOID NEAR RemoveIORB( PIORB pIORB, PIORB pIORBPrev, PPIORB pIORBFirst )
{
  PIORB         pIORBNext;

  pIORBNext = (pIORB->RequestControl & IORB_CHAIN) ? pIORB->pNxtIORB : 0;

  if ( pIORBPrev )
  {
    pIORBPrev->pNxtIORB = pIORBNext;
  }
  else
  {
    *pIORBFirst = pIORBNext;
  }

  return( pIORBNext );
}

