/*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 = 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"
#include "scsi.h"                                                              /*@V151345*/

#define INCL_INITRP_ONLY
#include "reqpkt.h"

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

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

/*---------------------------------------------------------*/
/*  SuspendIORBReq()                                       */
/*                                                         */
/*  This routine handles suspend IORBs. The IORB is queued */
/*  on the HWResource structure for the UNIT it is         */
/*  directed to.                                           */
/*                                                         */
/*  Called from PreProcessIORBs(); this IORB is placed on  */
/*  the HWResources's suspend IORB queue.                  */
/*---------------------------------------------------------*/

VOID NEAR SuspendIORBReq( NPACB npACB, PIORB pNewIORB )
{
   USHORT DCFlags = ((PIORB_DEVICE_CONTROL)pNewIORB)->Flags;

   pNewIORB->pNxtIORB = 0;


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

   DISABLE
   if( npACB->pSuspendHead )                                        /*@V147576*/
   {
      if( DCFlags & DC_SUSPEND_IMMEDIATE )
      {
         pNewIORB->pNxtIORB   = npACB->pSuspendHead;                /*@V147576*/
         npACB->pSuspendHead  = pNewIORB;                           /*@V147576*/
      }
      else
      {
         npACB->pSuspendFoot->pNxtIORB = pNewIORB;                  /*@V147576*/
         npACB->pSuspendFoot = pNewIORB;                            /*@V147576*/
      }
   }

   /*----------------------------------*/
   /*  Put first suspend iorb on Queue */
   /*----------------------------------*/

   else
   {
      npACB->pSuspendHead = pNewIORB;                               /*@V147576*/
      npACB->pSuspendFoot = pNewIORB;                               /*@V147576*/
      if( DCFlags & DC_SUSPEND_IMMEDIATE )
      {
         npACB->CountUntilSuspend = IMMEDIATE_COUNT;                /*@V147576*/
      }
      else
      {
         npACB->CountUntilSuspend = DEFERRED_COUNT;                 /*@V147576*/
      }
   }
   ENABLE
}

/*-----------------------------------------------------------*/
/*  Suspend()                                                */
/*                                                           */
/*  Process a suspend IORB.  Leave the HWResources allocated */
/*  and the state machine active and waited, just like       */
/*  during interrupt processing.  Notify the requestor the   */
/*  suspend request was approved.                            */
/*-----------------------------------------------------------*/
VOID NEAR Suspend( NPACB npACB )
{
  USHORT        Flags;
  NPHWRESOURCE  npHWR = &HWResource[npACB->HWResourceIndex];        /*@V147576*/
  PIORB         pIORB = npACB->pIORB;                               /*@V147576*/

  Flags = ((PIORB_DEVICE_CONTROL)pIORB)->Flags;

  if( npHWR->npOwnerACB == npACB )                                  /*@V147576*/
  {                                                                /*@V93531*/
    npHWR->SuspendIRQaddr =                                         /*@V147576*/
               ((PIORB_DEVICE_CONTROL)pIORB)->IRQHandlerAddress;   /*@V93531*/
  }                                                                /*@V93531*/
  else                                                             /*@V93531*/
  {
    npACB->IORBStatus |= IORB_ERROR;
    npACB->IORBError   = IOERR_CMD_SYNTAX;
  }

  /*                                                                  @V147567
  ** The BIOS may have turned off the IDE interface, so reactivate    @V147567
  ** the interface before notifying the requester.                    @V147567
  */                                                                /*@V147567*/
  if ( npACB->Flags & ACBF_PS2IDEPORT  )                            /*@V147567*/
  {                                                                 /*@V147567*/
    if ( BIOSActive )                                               /*@V147567*/
    {                                                               /*@V147567*/
      SetupPS2IDEPort( npACB->IOPorts[FI_PDAT], ON );               /*@V147567*//*@V153916*/
    }                                                               /*@V147567*/
  }                                                                 /*@V147567*/

  /*                                                                  @V147576
  ** Goto sleep in the ACBS_SUSPEND state and wait for a resume       @V147576
  ** IORB.  The HW Resource remains allocated during a suspend.       @V147576
  ** Note the ACBF_SM_ACTIVE flag is NOT being turned off             @V147576
  ** while suspended.  This is to prevent any new IORB requests       @V147576
  ** from restarting the state machine.  New requests are queued.     @V147576
  */                                                                /*@V147576*/
  npACB->Flags |= ACBF_WAITSTATE;                                   /*@V147576*/
  npHWR->Flags |= HWRF_SUSPENDED;                                   /*@V147576*/
  npACB->State = ACBS_SUSPEND;                                      /*@V147576*/

  IORBDone( npACB, pIORB );                                         /*@V147576*/

}

/*--------------------------------------------------------------*/  /*@V147576*/
/*  SuspendState()                                              */  /*@VVVVVVV*/
/*                                                              */
/*  The state machine enters ACBS_SUSPEND state as a result of  */
/*  processing a valid suspend request.  Once in ACBS_SUSPEND   */
/*  state the driver can do nothing except wait for a resume    */
/*  IORB and as such we should never reach this routine.        */
/*  (But, you would want to know about it if you ever did!)     */
/*--------------------------------------------------------------*/
VOID NEAR SuspendState( NPACB npACB )
{
   _asm int 3                                                       /*@AAAAAAA*/
}                                                                   /*@V147576*/

/*--------------------------------------------------------------*/  /*@V147576*/
/*  ResumeIORBReq()                                             */  /*@VVVVVVV*/
/*                                                              */
/*  Restart processing on the requested ACB's HW Resources.     */
/*  This request can always be handled immediately because      */
/*  once suspended, all we can do is wait to be resumed.  If    */
/*  the HW Resource is not currently suspended, it is an error. */
/*                                                              */
/*  Called from PreProcessIORBs(); this IORB is not placed on   */
/*  the ACB's IORB queue.                                       */
/*                                                              */
/*  Restart the current ACB's state machine which can do        */
/*  nothing until the HWResources for this RESUME operation are */
/*  freed.  FreeHWResources() starts the next waiting ACB.  The */
/*  next waiting ACB cannot be the current ACB because          */
/*  AllocateHWResources() does not get called until             */
/*  ACBF_SM_ACTIVE is reset and the state machine actually      */
/*  starts the IORB.  While this ACB was suspended, requests    */
/*  were queued to the ACB's IORB queue but not to the          */
/*  HWResource's ACB queue until the state machine is started   */
/*  and it is attempted to allocate the HW resources.           */
/*                                                              */
/*   StartState() -> AllocateHWResources()                      */
/*                                                              */
/*  The state machine remains active (ACBF_WAITSTATEed) while   */
/*  in the ACBS_SUSPEND state we are currently terminating.     */
/*  This is the same model followed for interrupt state.   The  */
/*  difference between suspend and interrupt states is that     */
/*  when interrupt state is continued there may be more work to */
/*  do but when a suspend state is resumed there is no more     */
/*  work to complete the current operation.  Hence the state    */
/*  can be forced to ACBS_START.                                */
/*--------------------------------------------------------------*/
VOID NEAR ResumeIORBReq( NPACB npACB, PIORB pIORB )
{
   NPHWRESOURCE npHWR = &HWResource[npACB->HWResourceIndex];

   if( npHWR->Flags & HWRF_SUSPENDED )
   {
#ifdef DEBUG
      if( npHWR->npOwnerACB != npACB )
      {
         _asm int 3
      }
      else if( npACB->pIORB )
      {
         _asm int 3
      }
      else if( !npHWR->SuspendIRQaddr )
      {
         _asm int 3
      }
      else if( npACB->State != ACBS_SUSPEND )
      {
         _asm int 3
      }
      else if( !(npACB->Flags & ACBF_SM_ACTIVE) )
      {
         _asm int 3
      }
      else if( npACB->pIORB )
      {
         _asm int 3
      }
#endif

      FreeHWResources( npACB );                                     /*@V127556*/
      npACB->State = ACBS_START;
      npACB->Flags &= ~ACBF_SM_ACTIVE;
   }
   else  /* the HW Resource was not suspended */
   {
#ifdef DEBUG
      _asm int 3
#endif
      pIORB->ErrorCode = IOERR_CMD_SYNTAX;
      pIORB->Status   |= IORB_ERROR;
   }

   return;                                                          /*@AAAAAAA*/
}                                                                   /*@V147576*/


/*--------------------------------------------------*/
/* ActivateACB                                      */
/*                                                  */
/* If the IRQ level is not hooked, hook it.         */
/*                                                  */
/*--------------------------------------------------*/
USHORT FAR ActivateACB( NPACB npACB )
{
  ULONG         PageListCount;
  USHORT        rc = 0;
  NPHWRESOURCE  npHWR;                                              /*@V147576*/

  DISABLE
  npHWR = &HWResource[npACB->HWResourceIndex];                      /*@V147576*/
  if ( npHWR->npOwnerACB == npACB )                                 /*@V147576*/
  {
#ifdef MCA                                                         /*@V117508*/
    rc = DevHelp_SetIRQ( (NPFN)   npACB->IRQEntry,                 /*@V117508*/
                           (USHORT) npACB->IntLevel,               /*@V117508*/
                           (USHORT) 1                );            /*@V117508*/
#else                                                              /*@V117508*/

    rc = DevHelp_SetIRQ( (NPFN)   npACB->IRQEntry,
                         (USHORT) npACB->IntLevel,
                         (USHORT) GetIRQSharedBit( npACB->IntLevel ));
#endif                                                             /*@V117508*/

  }

  ENABLE

  return ( rc );
}


/*---------------------------------------------------------*/
/* DeactivateACB                                           */
/*                                                         */
/*                                                         */
/*---------------------------------------------------------*/
VOID FAR DeactivateACB( NPACB npACB )
{
  NPHWRESOURCE  npHWR = &HWResource[npACB->HWResourceIndex];        /*@V147576*/

  if( npHWR->npOwnerACB == npACB )
  {
    DevHelp_UnSetIRQ( (USHORT) npACB->IntLevel );
    FreeHWResources( npACB );                                       /*@V147576*/
  }

}


/*-------------------------------------------------------*/
/* PreProcessIORBs                                       */
/*                                                       */
/* This filters incomming IORBs as they are passed       */
/* to the ADD driver.                                    */
/*                                                       */
/* Certain IORBs such as RESUME are removed              */
/* from the IORB stream and routed directly to their     */
/* handling routines.                                    */
/*                                                       */
/* The remaining IORBs are placed on the device queue.   */
/*                                                       */
/* SUSPEND IORBs are placed on the HWResource suspend    */
/* queue.                                                */
/*                                                       */
/*-------------------------------------------------------*/
PIORB NEAR PreProcessIORBs( NPACB npACB, NPUCB npUCB, PPIORB ppFirstIORB )
{
  PIORB         pIORB, pIORBPrev, pIORBNext;

  pIORB     = *ppFirstIORB;
  pIORBPrev = 0;

  do
  {
#ifdef BBR
    if( (pIORB->RequestControl & IORB_CHAIN) && pIORBNext == pIORB->pNxtIORB )
    {
       _asm int 3
    }
#endif
    pIORBNext = (pIORB->RequestControl & IORB_CHAIN) ? pIORB->pNxtIORB : 0;

    /*                                                              @V153620
    ** If the driver has been suspended by the kernel, reject all   @V153620
    ** work requests.                                               @V153620
    */                                                            /*@V153620*/
    if( OS2Suspend )                                              /*@V153620*/
    {                                                             /*@V153620*/
       /*                                                           @V153620
       ** This driver is suspended reject all requests until        @V153620
       ** resumed.                                                  @V153620
       */                                                         /*@V153620*/
       pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
       pIORB->ErrorCode = IOERR_CMD_ABORTED;                      /*@V153620*/
       pIORB->Status    = (IORB_ERROR | IORB_DONE);               /*@V153620*/
#ifdef DEBUG                                                        /*@V153620*/
       _asm int 3                                                 /*@V153620*/
#endif                                                              /*@V153620*/
       PreProcessedIORBDone( pIORB );
    }                                                             /*@V153620*/

#ifdef BBR
    // If this is a write to any block specified in a     block sequence
    // then return a fake CRC error.
    else if( npUCB->nBBRSequences > 0 &&
             pIORB->CommandCode == IOCC_EXECUTE_IO &&
             (pIORB->CommandModifier == IOCM_WRITE ||
              pIORB->CommandModifier == IOCM_WRITE_VERIFY) )
    {
       USHORT   i;
       ULONG    j;
       ULONG    firstRequestBlock = ((PIORB_EXECUTEIO)(pIORB))->RBA;
       ULONG    lastRequestBlock  = ((PIORB_EXECUTEIO)(pIORB))->RBA  + ((PIORB_EXECUTEIO)(pIORB))->BlockCount;

       for( i = 0; i < npUCB->nBBRSequences; i++ )
       {
          for( j = firstRequestBlock; j < lastRequestBlock; j++ )
          {
             if( j >= npUCB->BBRSequence[i].startBlock &&
                 j < (npUCB->BBRSequence[i].startBlock + npUCB->BBRSequence[i].blockCount) )
             {
                pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
                pIORB->ErrorCode = IOERR_RBA_CRC_ERROR;
                pIORB->Status    = (IORB_ERROR | IORB_DONE);
                if( npUCB->BBRFlags & BBRF_INT3 )
                {
                   _asm
                   {
                      // Show requested RBA that triggered this error.  The instruction
                      // prefix, 0x66, causes the prefaced instructions to operate on 32-bit
                      // data and the entire eax register instead of just ax.
                      _emit  0x66
                      push   ax
                      _emit  0x66
                      mov    ax, j
                      int    3
                      _emit  0x66
                      pop    ax
                   }
                }
                PreProcessedIORBDone( pIORB );
                goto foundBB;
             }
          }
       }
foundBB:
       ;
    }
#endif

    else
    {
    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*/
            PreProcessedIORBDone( pIORB );                          /*@V147576*/
            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 );
            PreProcessedIORBDone( pIORB );                          /*@V147576*/
            continue;

          case IOCM_GET_QUEUE_STATUS:
            pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
            GetQueueStatus( npACB, pIORB );
            PreProcessedIORBDone( pIORB );                          /*@V147576*/
            continue;

          case IOCM_LOCK_MEDIA:                                     /*@V151345*/
          case IOCM_UNLOCK_MEDIA:                                   /*@VVVVVVV*/
          case IOCM_EJECT_MEDIA:
            pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
            ProcessLockUnLockEject( npACB, pIORB,                   /*@V155162*/
                                    pIORB->CommandModifier);        /*@V155162*/
            PreProcessedIORBDone( pIORB );
            continue;
        }
        break;

    case IOCC_ADAPTER_PASSTHRU:
      {
      PIORB_ADAPTER_PASSTHRU pIORBP=(PIORB_ADAPTER_PASSTHRU)pIORB;
      switch ( pIORB->CommandModifier )
        {
        case IOCM_EXECUTE_CDB:
          {
          switch(pIORBP->pControllerCmd[0])
            {
//
//          Vince, we really need this function to be supported
//          but I don't know how to do it for generic ATA/ATAPI devices
//
//            case SCSI_TEST_UNIT_READY:
//              break;
            case SCSI_START_STOP_UNIT:
              if(pIORBP->pControllerCmd[4]==2)
                {
                pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
                ProcessLockUnLockEject( npACB, pIORBP,              /*@V155162*/
                                        IOCM_EJECT_MEDIA);          /*@V155162*/
                PreProcessedIORBDone( pIORB );
                continue;
                } /* endif */
              break;
            case SCSI_LOCK_UNLOCK:
                pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
                ProcessLockUnLockEject( npACB, pIORBP,              /*@V155162*/
                                        pIORBP->pControllerCmd[4] & 1 ?   /*@V155162*/
                                           IOCM_LOCK_MEDIA :        /*@V155162*/
                                           IOCM_UNLOCK_MEDIA );     /*@V155162*/
                PreProcessedIORBDone( pIORB );
                continue;
              break;
            case SCSI_INQUIRY:
                {
                PSCSI_INQDATA pinqbuffer;
                USHORT i;

                pIORBNext = RemoveIORB( pIORB, pIORBPrev, ppFirstIORB );
                if(!GetInquiryData((NPUCB)(pIORB->UnitHandle),(PBYTE)&npACB->identifyBuf, /*@V154814*/
                                   sizeof(npACB->identifyBuf)))                           /*@V154814*/
                {
                  if(!DevHelp_PhysToVirt(pIORBP->pSGList->ppXferBuf,
                              (USHORT)pIORBP->pSGList->XferBufLen,  /*@V152648*/
                              &pinqbuffer,&i))
                  {
                    strnswap(pinqbuffer->VendorID,npACB->identifyBuf.ModelNum,sizeof(pinqbuffer->VendorID)+sizeof(pinqbuffer->ProductID));
                    strnswap(pinqbuffer->ProductRev,npACB->identifyBuf.FirmwareRN,sizeof(pinqbuffer->ProductRev));
                  } /* endif */
                  else
                  {
                    CmdNotSupported(npACB);
                  } /* endelse */
                }
                PreProcessedIORBDone( pIORB );
                continue;
                }
              break;
            default:
              break;
            } /* endswitch */
          }
          break;
        }
      }                                                               /*@AAAAAAA*/
    }                                                                 /*@V151345*/
    }
    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 );
}


/*-------------------------------------------------------*/         /*@V147576*/
/* AllocateHWResources                                   */         /*@VVVVVVV*/
/*                                                       */
/* This routine controls sharing of Hardware Resources,  */
/* HWResource[], between two or more controllers which   */
/* may share these resources.                            */
/*                                                       */
/* If we are in a 'shared' scenario, this routine        */
/* serializes the requestors of the shared resource.     */
/*                                                       */
/* A requesting ACB is added to the HWResource wait      */
/* only if it not the current owner of the HWResource.   */
/*                                                       */
/*-------------------------------------------------------*/
USHORT NEAR AllocateHWResources( NPACB npACB )
{
  NPHWRESOURCE  npHWR;
  USHORT        ResourceIndex;
  USHORT        rc = 0;

  npHWR = &HWResource[npACB->HWResourceIndex];

  npACB->npNextACB = 0;

  DISABLE

  /*------------------------------------------*/
  /* If we are SUSPENDED, then indicate that  */
  /* resources were not allocated to the      */
  /* requestor.                               */
  /*------------------------------------------*/
  if( npHWR->Flags & HWRF_SUSPENDED )
  {
    rc = 1;
  }

  /*------------------------------------------------*/
  /* If there is no current owner for the resources */
  /* make the requesting ACB the current owner      */
  /*------------------------------------------------*/
  else if( !npHWR->npOwnerACB )
  {
    npHWR->npOwnerACB = npACB;
  }

  /*-----------------------------------------------*/
  /* If someone is requesting resources and they   */
  /* do not match the current owner, then they get */
  /* queued.                                       */
  /*-----------------------------------------------*/
  else if( npHWR->npOwnerACB != npACB )
  {
    rc = 1;
  }

  /*---------------------------------------------*/
  /* If rc != 0 as determined by the logic above */
  /* the requestor must be blocked.              */
  /*                                             */
  /* The requesting ACB is placed on a queue for */
  /* the resource.                               */
  /*---------------------------------------------*/
  if( rc )
  {
    if( npHWR->npFootACB )
    {
      npHWR->npFootACB->npNextACB = npACB;
    }
    else
    {
      npHWR->npHeadACB = npACB;
    }
    npHWR->npFootACB = npACB;

    npACB->Flags |= ACBF_WAITSTATE;
  }

  ENABLE

  return( rc );                                                     /*@AAAAAAA*/
}                                                                   /*@V147576*/


/*--------------------------------------------------*/              /*@V147576*/
/* FreeHWResources                                  */              /*@VVVVVVV*/
/*                                                  */
/* This routine releases Hardware Resources owned   */
/* by an ACB.                                       */
/*                                                  */
/* If there are other ACBs waiting and an Immediate */
/* Suspend is not pending, then restart processing  */
/* of the queued requestor for the resources.       */
/*                                                  */
/*--------------------------------------------------*/
NPACB NEAR FreeHWResources( NPACB npACB )
{
  NPHWRESOURCE  npHWR;

  npHWR = &HWResource[npACB->HWResourceIndex];

  DISABLE

#ifdef DEBUG
  if( npHWR->npOwnerACB != npACB )
  {
     ENABLE
     _asm int 3
  }
#endif

  npHWR->SuspendIRQaddr = 0L;
  npHWR->Flags &= ~HWRF_SUSPENDED;

  if( npHWR->npOwnerACB = npHWR->npHeadACB )
  {
    if( npHWR->npHeadACB = npHWR->npOwnerACB->npNextACB )
      npHWR->npOwnerACB->npNextACB = 0;
    else
      npHWR->npFootACB = 0;

    /*
    ** Start the state machine for the next waiting ACB.  Set the
    ** SM_ACTIVE flag because this may be starting an ACB that is
    ** currently ACBF_WAITSTATEed.
    */
    npHWR->npOwnerACB->Flags |= ACBF_SM_ACTIVE;
    ENABLE

#ifdef DEBUG
    if( npHWR->npOwnerACB == npACB )
    {
       _asm int 3
    }
    if( npHWR->npOwnerACB->State != ACBS_START )
    {
       _asm int 3
    }
#endif

    StartSM( npHWR->npOwnerACB );
  }
  ENABLE

  return( npHWR->npOwnerACB );                                      /*@AAAAAAA*/
}                                                                   /*@V147576*/
