/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = SCSIREQ2.C
 *
 * DESCRIPTIVE NAME = IBM2SCSI.ADD - Adapter Driver for IBM SCSI adapters.
 *                    IORB processing, file #2.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION  This file implements the following IORB processing routines:
 *
 *              IOCC_FORMAT
 *              IOCC_UNITSTATUS
 *              IOCC_ADAPTERPASSTHRU
 *              IOCC_UNDEFINED       (for undefined IORBs)
 *
 *
*/

/*----------------------*/
/* System include files */
/*----------------------*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"

/*----------------------*/
/* ADD include files    */
/*----------------------*/

#include "strat2.h"
#include "dhcalls.h"
#include "iorb.h"
#include "scb.h"
#include "reqpkt.h"
#include "abios.h"
#include "scsi.h"

/*----------------------*/
/* Local include files  */
/*----------------------*/

#include "mmscb.h"
#include "delivery.h"
#include "scsiadd.h"
#include "scsiextn.h"
#include "scsipro.h"


/*************************************************************************/
/*                                                                       */
/* IOCC_Format                                                           */
/*                                                                       */
/*    This routine handles the format IORB.  We expect a CDB to be sent  */
/*    to us.  Since the format of the Format IORB is laid out the same   */
/*    as a passthru CDB, we let the setup CDB routine handle getting it  */
/*    ready for us.  This routine is for locate mode devices.            */
/*                                                                       */
/*       Entry:  IOCC_Format(pFormat, npDCB)                             */
/*                                                                       */
/*               pFormat - format IORB pointer                           */
/*               npDCB - DCB                                             */
/*                                                                       */
/*       Exit: none.                                                     */
/*                                                                       */
/*************************************************************************/

VOID IOCC_Format(PIORB_FORMAT pFormat, NPDCB npDCB)
{

   USHORT temp;                       /* DO NOT REMOVE !! */

   /*-----------------------------------------------------------------*/
   /* Use the IOCC_SetupPassthruCDB routine to setup the request. The */
   /* Format media command passes us a CDB.  If the setup shows it    */
   /* ready then start it if the device is IDLE.  If not then put it  */
   /* in the queue.                                                   */
   /*-----------------------------------------------------------------*/

   IORBWRKSP(pFormat,Status) |= IORBNOGROUP;
   IORBWRKSP(pFormat,Status) &= ~IORBCALLOUT;

   if ((IOCC_SetupPassthruCDB((PIORB_ADAPTER_PASSTHRU)pFormat, npDCB)) == READY) {

      SAVEIF();

      if (npDCB->state == IDLE) {
         StartIORB((PIORB)pFormat, npDCB);
      }
      else {
         QueueIORB((PIORB)pFormat, npDCB, BACK);
      }

      RESTOREIF();
   }
}

/*************************************************************************/
/*                                                                       */
/* IOCC_MM_Format                                                        */
/*                                                                       */
/*    This routine handles the move mode format IORB.  A CDB is sent as  */
/*    part of the command.  We package it up in an execute CDB element   */
/*    and send it down the pipe.                                         */
/*                                                                       */
/*       Entry:  IOCC_Format(pFormat, npDCB)                             */
/*                                                                       */
/*               pFormat - format IORB pointer                           */
/*               npDCB - DCB                                             */
/*                                                                       */
/*       Exit: none.                                                     */
/*                                                                       */
/*************************************************************************/

VOID IOCC_MM_Format(PIORB_FORMAT pFormat, NPDCB npDCB)

{
   USHORT      i, cCDB;
   PBYTE       pCDB;
   CE_EXEC_CDB ce_execcdb;

   /*----------------------------------------------------*/
   /* Initialize the control element and IORB workspace. */
   /*----------------------------------------------------*/

   INITCTRLEL(ce_execcdb,0,FN_SENDSCSI,npDCB,pFormat);
   INITARSENSE(ce_execcdb,pFormat,npDCB);
   IORBWRKSP2(pFormat,cEl) = 1;
   IORBWRKSP2(pFormat,Status) &= ~IORBCALLOUT;

   /*---------------------------------------------------------------*/
   /* Copy the CDB, for the format command, to the control element. */
   /* Then copy any SG list (for defect lists) and calc the size of */
   /* element.  Then start the element.                             */
   /*---------------------------------------------------------------*/

   pCDB = pFormat->pFormatCmd;
   cCDB = pFormat->FormatCmdLen;

   for (i=0; i<cCDB; i++) {
      ce_execcdb.cdb[i] = *(pCDB + i);
   }

   ce_execcdb.cBytes      = (ULONG)CountSGBlocks(pFormat->pSGList,
                                                 pFormat->cSGList, 1);
   ce_execcdb.ReqH.optctrl |= cCDB << 8;  /* cdb length  */

   ce_execcdb.ReqH.length = sizeof(CE_EXEC_CDB) - (17 * sizeof(SCATGATENTRY));

   StartCtrlEl((PREQH)&ce_execcdb, npDCB);

}

/*************************************************************************/
/*                                                                       */
/* IOCC_UnitStatus                                                       */
/*                                                                       */
/*    This routine handles the UnitStatus IORB.  It is for both locate   */
/*    and move mode.                                                     */
/*                                                                       */
/*       Entry:  IOCC_UnitStatus(pStatus, npDCB)                         */
/*                                                                       */
/*               pStatus - ptr to the IORB.                              */
/*               npDCB - DCB                                             */
/*                                                                       */
/*       Exit:                                                           */
/*                                                                       */
/*************************************************************************/

VOID IOCC_UnitStatus(PIORB_UNIT_STATUS pStatus, NPDCB npDCB)
{

   USHORT rc;

   switch (pStatus->iorbh.CommandModifier) {

      /*---------------------------------------------------------*/
      /* For a get unit status a test unit ready is sent to the  */
      /* device, unless the device is defective.  For move mode  */
      /* the move mode routine is called and the element will be */
      /* enqueued.  For locate mode the setup routine is called. */
      /* If setup is OK and device is IDLE then the SCB will be  */
      /* started, otherwise it is queued.                        */
      /*---------------------------------------------------------*/

      case IOCM_GET_UNIT_STATUS:

         if (npDCB->UnitInfo.UnitFlags & UF_DEFECTIVE) {
            pStatus->UnitStatus = US_DEFECTIVE;
            FinishIORB((PIORB)pStatus, npDCB);
         }
         else {
            if (ACBTbl[npDCB->ai].status & MOVEMODE) {
               IOCC_MM_UnitStatus(pStatus, npDCB);
            }
            else {
               rc = IOCC_SetupUnitStatus(pStatus, npDCB);

               SAVEIF();
               if (rc == READY && npDCB->state == IDLE) {
                  StartIORB((PIORB)pStatus, npDCB);
               }
               else {
                  QueueIORB((PIORB)pStatus, npDCB, BACK);
               }
               RESTOREIF();
            }
         }
         break;


      /*----------------------------------------*/
      /* Get changeline state is not supported. */
      /*----------------------------------------*/

      case IOCM_GET_CHANGELINE_STATE:

         pStatus->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
         FinishIORB((PIORB)pStatus, npDCB);
         break;


      /*----------------------------------------*/
      /* Get media sense will return unknown.   */
      /*----------------------------------------*/

      case IOCM_GET_MEDIA_SENSE:

         pStatus->UnitStatus = US_MEDIA_UNKNOWN;
         FinishIORB((PIORB)pStatus, npDCB);
         break;


      /*----------------------------------------*/
      /* Get lock status returns lock status.   */
      /*----------------------------------------*/

      case IOCM_GET_LOCK_STATUS:

         pStatus->UnitStatus = (npDCB->status & UNITLOCKED) ? US_LOCKED : 0;
         FinishIORB((PIORB)pStatus, npDCB);
         break;

   }
}


/*************************************************************************/
/*                                                                       */
/* IOCC_SetupUnitStatus                                                  */
/*                                                                       */
/*    This routine will setup the testunit ready and all callout addrs.  */
/*                                                                       */
/*       Entry:  IOCC_SetupUnitStatus(pStatus, npDCB)                    */
/*                                                                       */
/*               pStatus - ptr to the IORB.                              */
/*               npDCB - DCB                                             */
/*                                                                       */
/*       Exit:   READY - request is ready to go.                         */
/*               NORESOURCE - unable to allocate resource.               */
/*                                                                       */
/*                                                                       */
/*************************************************************************/

USHORT IOCC_SetupUnitStatus(PIORB_UNIT_STATUS pStatus, NPDCB npDCB)

{

   NPSCBH npSCBH;

   /*--------------------------------------------------------------*/
   /* Set the IORB status so that the callout will be done even on */
   /* error.  This still allows us to set the correct bits in the  */
   /* completion routine.  Also disable any retries.               */
   /*--------------------------------------------------------------*/

   IORBWRKSP(pStatus,Status) &= ~IORBINCOMPLT;
   IORBWRKSP(pStatus,Status) |= IORBCALLOUT + IORBCALLOUTERR;

   pStatus->iorbh.RequestControl |= IORB_DISABLE_RETRY;

   /*-------------------------------------------------------------------*/
   /* Allocate and build the SCB.  Then setup the CDB and return READY. */
   /*-------------------------------------------------------------------*/

   if ((npSCBH = AllocSCB(npDCB, (PIORB)pStatus)) != NULL) {
      BuildSCB(&(npSCBH->scb), SCB_SENDOTHER, npDCB, 6L, 0L, 0L, 0, 0);

      npSCBH->scb.EXT.CDB.SCSIcdb[0] = SCSI_TEST_UNIT_READY;
      npSCBH->scb.EXT.CDB.SCSIcdb[1] = npDCB->UnitInfo.UnitSCSILUN << 5;
      npSCBH->scb.EXT.CDB.SCSIcdb[2] = 0;
      npSCBH->scb.EXT.CDB.SCSIcdb[3] = 0;
      npSCBH->scb.EXT.CDB.SCSIcdb[4] = 0;
      npSCBH->scb.EXT.CDB.SCSIcdb[5] = 0;

      npSCBH->scb.Enable &= ~(SCBEfPT | SCBEfRE);

      IORBWRKSP(pStatus,npfnCallOut) = IOCC_UnitStatusDone;
      IORBWRKSP(pStatus,Status)     |= IORBSCBLONG;
      IORBWRKSP(pStatus,npSCBH)      = npSCBH;
      IORBWRKSP(pStatus,cSCBs)       = 1;

      return(READY);

   }

   /*-------------------------------------------------------------*/
   /* If SCB no allocated then set status and return NORESOURCE.  */
   /*-------------------------------------------------------------*/

   else {

      IORBWRKSP(pStatus,Status)  |= IORBINCOMPLT;
      IORBWRKSP(pStatus,npfnCallOut) = IOCC_SetupUnitStatus;
      return(NORESOURCE);
   }
}

/*************************************************************************/
/*                                                                       */
/* IOCC_MM_UnitStatus                                                    */
/*                                                                       */
/*    This routine will setup a control element to send a test unit      */
/*    ready to the specified device.                                     */
/*                                                                       */
/*       Entry:  IOCC_MM_UnitStatus(pStatus, npDCB)                      */
/*                                                                       */
/*               pStatus - ptr to the IORB.                              */
/*               npDCB - DCB                                             */
/*                                                                       */
/*       Exit:   NONE.                                                   */
/*                                                                       */
/*************************************************************************/

VOID IOCC_MM_UnitStatus(PIORB_UNIT_STATUS pStatus, NPDCB npDCB)

{

   CE_EXEC_CDB ce_execcdb;

   /*---------------------------------*/
   /* Initialize the control element. */
   /*---------------------------------*/

   INITCTRLEL(ce_execcdb,0,FN_SENDSCSI,npDCB,pStatus);
   INITARSENSE(ce_execcdb,pStatus,npDCB);

   /*---------------------------------------------------------*/
   /* Set the IORB so that the correct callout will be made.  */
   /*---------------------------------------------------------*/

   IORBWRKSP(pStatus,Status)      |= IORBCALLOUT + IORBCALLOUTERR;
   IORBWRKSP(pStatus,npfnCallOut)  = IOCC_UnitStatusDone;
   pStatus->iorbh.RequestControl  |= IORB_DISABLE_RETRY;

   /*--------------------------------------*/
   /* Build the CDB and start the element. */
   /*--------------------------------------*/

   ce_execcdb.cdb[0]        = 0;
   ce_execcdb.cdb[1]        = npDCB->UnitInfo.UnitSCSILUN << 5;
   ce_execcdb.cdb[2]        = 0;
   ce_execcdb.cdb[3]        = 0;
   ce_execcdb.cdb[4]        = 0;
   ce_execcdb.cdb[5]        = 0;
   ce_execcdb.ReqH.length   = sizeof(CE_EXEC_CDB) - (17*sizeof(SCATGATENTRY));
   ce_execcdb.ReqH.optctrl |= 0x0600;
   ce_execcdb.cBytes        = 0;

   StartCtrlEl((PREQH)&ce_execcdb, npDCB);

}



/*************************************************************************/
/*                                                                       */
/* IOCC_UnitStatusDone                                                   */
/*                                                                       */
/*    This routine completes the IOCCUnitStatus call for get unit status.*/
/*                                                                       */
/*       Entry:  IOCC_UnitStatusDone(pStatus, npDCB)                     */
/*                                                                       */
/*       Exit:   COMPLETE - indicates that this request is complete      */
/*                                                                       */
/*       Notes: This routine could be called at interrupt time.          */
/*              This routine is a call-out routine.                      */
/*                                                                       */
/*************************************************************************/

USHORT IOCC_UnitStatusDone(PIORB_UNIT_STATUS pStatus, NPDCB npDCB)

{

   /*---------------------------------------------------------------*/
   /* If the error code is 0 then the device is OK, if not then the */
   /* device is not ready.                                          */
   /*---------------------------------------------------------------*/

   if (pStatus->iorbh.ErrorCode) {
      pStatus->UnitStatus = 0;
   }
   else {
      pStatus->UnitStatus = US_READY+US_POWER;
   }

   return(COMPLETE);

}



/*************************************************************************/
/*                                                                       */
/* IOCC_AdapterPassthru                                                  */
/*                                                                       */
/*    This routine handles the adapter passthru command for locate mode. */
/*                                                                       */
/*       Entry:  IOCC_AdapterPassthru(pPassthru, npDCB)                  */
/*                                                                       */
/*               pPassthru- ptr to the IORB.                             */
/*               npDCB    - ptr to the DCB.                              */
/*                                                                       */
/*       Exit:                                                           */
/*                                                                       */
/*************************************************************************/

VOID IOCC_AdapterPassthru(PIORB_ADAPTER_PASSTHRU pPassthru, NPDCB npDCB)
{

   USHORT rc;

   /*-------------------------------------------------------------*/
   /* Set the IORB status to not chain this IORB and there is no  */
   /* local callout required.  Then if the passthru is a CDB try  */
   /* to setup the request by calling the setup routine.  The rc  */
   /* is saved.  If passthru is for an SCB we don't have any real */
   /* local setup/resources to deal with so set the local status  */
   /* and set the rc to show everythin honky dory.                */
   /*-------------------------------------------------------------*/

   IORBWRKSP(pPassthru,Status) |= IORBNOGROUP;
   IORBWRKSP(pPassthru,Status) &= ~IORBCALLOUT;

   if (pPassthru->iorbh.CommandModifier == IOCM_EXECUTE_CDB) {
      rc = IOCC_SetupPassthruCDB(pPassthru, npDCB);
   }
   else {
      rc = READY;
      IORBWRKSP(pPassthru,Status) |= IORBPASSTHRUSCB;
   }

   /*---------------------------------------------------------------*/
   /* Now determine if the request should be started now or queued. */
   /*---------------------------------------------------------------*/

   SAVEIF();

   if (rc == READY && npDCB->state == IDLE) {
      StartIORB((PIORB)pPassthru, npDCB);
   }
   else {
      QueueIORB((PIORB)pPassthru, npDCB, BACK);
   }

   RESTOREIF();

}


/*************************************************************************/
/*                                                                       */
/* IOCC_SetupPassthruCDB                                                 */
/*                                                                       */
/*    This routine sets up the request to passthru a CDB to the device.  */
/*    It is used for both the IOCC Adapter passthru command and the IOCC */
/*    format command.  Both pass a CDB as a parameter in the IORB.       */
/*    For passthru and format support we take advantage of the same lay- */
/*    out of the format and passthru IORBs.                              */
/*    This routine is for locate mode devices.                           */
/*                                                                       */
/*       Entry:  IOCC_SetupPassThruCDB (pPassthru, npDCB)                */
/*                                                                       */
/*               pPassthru- ptr to the IORB.                             */
/*               npDCB    - ptr to the DCB.                              */
/*                                                                       */
/*       Exit:   READY if scb allocated, NORESOURCE if not.              */
/*                                                                       */
/*************************************************************************/

USHORT IOCC_SetupPassthruCDB(PIORB_ADAPTER_PASSTHRU pPassthru, NPDCB npDCB)

{

   NPSCBH npSCBH;
   USHORT i, cCDB;
   PBYTE  pCDB;

   IORBWRKSP(pPassthru,Status)  &= ~IORBINCOMPLT;

   /*-------------------------------------------------------------------*/
   /* Allocate an SCB for so we can send the CDB.  If it was allocated  */
   /* then continue setting up the command.                             */
   /*-------------------------------------------------------------------*/

   if ((npSCBH = AllocSCB(npDCB, (PIORB)pPassthru)) != NULL) {

      /*----------------------------------------------------------*/
      /* The format and passthru IORBs have the same layout.  We  */
      /* don't have to worry about which one it is while building */
      /* the SCB.  Set status bits in the SCB accordingly.        */
      /*----------------------------------------------------------*/

      pCDB = pPassthru->pControllerCmd;
      cCDB = pPassthru->ControllerCmdLen;
      if (pPassthru->cSGList == 1) {
         BuildSCB(&(npSCBH->scb), SCB_SENDOTHER, npDCB, (ULONG)cCDB,
               pPassthru->pSGList->ppXferBuf,
               pPassthru->pSGList->XferBufLen, 0, 0);
         npSCBH->scb.Enable &= ~SCBEfPT;
      }
      else {
         BuildSCB(&(npSCBH->scb), SCB_SENDOTHER, npDCB, (ULONG)cCDB,
              pPassthru->ppSGLIST,
              pPassthru->cSGList*sizeof(SCATGATENTRY),
              0, 0);
      }

      if (pPassthru->iorbh.CommandCode == IOCC_ADAPTER_PASSTHRU) {
         npSCBH->scb.Enable |= (pPassthru->Flags & PT_DIRECTION_IN) ?
                               SCBEfRD : 0;
      }

      if (pPassthru->cSGList == 0) {
         npSCBH->scb.Enable &= ~SCBEfPT;
      }

      /*--------------------------------------------------*/
      /* Copy the CDB to the SCB and set the IORB status. */
      /* Then return READY.                               */
      /*--------------------------------------------------*/

      for (i=0; i<cCDB; i++) {
         npSCBH->scb.EXT.CDB.SCSIcdb[i] = *(pCDB + i);
      }

      IORBWRKSP(pPassthru,Status) |= IORBSCBLONG;
      IORBWRKSP(pPassthru,npSCBH)  = npSCBH;
      IORBWRKSP(pPassthru,cSCBs)   = 1;
      IORBWRKSP(pPassthru,Status) &= ~IORBCALLOUT;
      return(READY);

   }

   /*-------------------------------------------------------------------*/
   /* Otherwise no SCB was allocated so set the IORB status accordingly */
   /* and return NORESOURCE.                                            */
   /*-------------------------------------------------------------------*/

   else {
      IORBWRKSP(pPassthru,Status)     |= IORBCALLOUT+IORBINCOMPLT;
      IORBWRKSP(pPassthru,npfnCallOut) = IOCC_SetupPassthruCDB;
      return(NORESOURCE);
   }
}

/*************************************************************************/
/*                                                                       */
/* IOCC_MM_AdapterPassthru                                               */
/*                                                                       */
/*    This routine handles the adapter passthru command for move mode.   */
/*                                                                       */
/*       Entry:  IOCC_MM_AdapterPassthru(pPassThru, npDCB)               */
/*                                                                       */
/*               pPassThru- ptr to the IORB.                             */
/*               npDCB    - ptr to the DCB.                              */
/*                                                                       */
/*       Exit:                                                           */
/*                                                                       */
/*************************************************************************/

VOID IOCC_MM_AdapterPassthru(PIORB_ADAPTER_PASSTHRU pPassthru, NPDCB npDCB)
{

   USHORT      i, cCDB;
   PBYTE       pCDB;
   CE_EXEC_CDB ce_execcdb;        /* buffer for largest possible ce  */

   /*---------------------------------------------------*/
   /* Intialize the control element and IORB workspace. */
   /*---------------------------------------------------*/

   INITCTRLEL(ce_execcdb,0,0,npDCB,pPassthru);
   INITARSENSE(ce_execcdb,pPassthru,npDCB);
   IORBWRKSP2(pPassthru,Status) &= ~IORBCALLOUT;

   /*--------------------------------------------------------------*/
   /* For a CDB format the control element as an execute SCSI req  */
   /* element and copy the CDB to the element.  Copy the SG list.  */
   /*--------------------------------------------------------------*/

   if (pPassthru->iorbh.CommandModifier == IOCM_EXECUTE_CDB) {

      pCDB = pPassthru->pControllerCmd;
      cCDB = pPassthru->ControllerCmdLen;

      for (i=0; i<cCDB; i++) {
         ce_execcdb.cdb[i] = *(pCDB + i);
      }

      ce_execcdb.ReqH.ci     = FN_SENDSCSI;
      ce_execcdb.cBytes      = (ULONG)CountSGBlocks(pPassthru->pSGList,
                                                    pPassthru->cSGList, 1);
      ce_execcdb.ReqH.optctrl |= cCDB << 8;  /* cdb length  */

      for (i=0; i<pPassthru->cSGList; i++) {
         ce_execcdb.SGList[i].ppXferBuf  = ((pPassthru->pSGList)+i)->ppXferBuf;
         ce_execcdb.SGList[i].XferBufLen = ((pPassthru->pSGList)+i)->XferBufLen;
      }

      ce_execcdb.ReqH.optctrl |=
         (pPassthru->Flags & PT_DIRECTION_IN) ? OP_RD : 0;

      ce_execcdb.ReqH.length = sizeof(CE_EXEC_CDB) -
         ( (17 - pPassthru->cSGList) * sizeof(SCATGATENTRY));

   }

   /*-----------------------------------------------------------------*/
   /* If an SCB then format the element as an execute locate mode SCB */
   /* element.  Put the phys addr of the SCB in the contol element.   */
   /*-----------------------------------------------------------------*/

   else {
      ce_execcdb.ReqH.length       = sizeof(CE_EXEC_SCB);
      ce_execcdb.ReqH.ci           = FN_EXECSCB;
      (ULONG)(ce_execcdb.cdb[0])   = pPassthru->ppSCB;
      ce_execcdb.ReqH.optctrl     |= CMD_NORMSCB;
      IORBWRKSP(pPassthru,Status) |= IORBPASSTHRUSCB;
      ce_execcdb.ReqH.optctrl     &= ~OP_ND;
      ce_execcdb.ReqH.optctrl     |= TO_MOVEMODE;      /* @x08084 */
   }

   /*-------------------*/
   /* Start the element */
   /*-------------------*/

   StartCtrlEl((PREQH)&ce_execcdb, npDCB);

}



/*************************************************************************/
/*                                                                       */
/* IOCC_Undefined                                                        */
/*                                                                       */
/*    This routine handles undefined IORBs by returning an errorcode.    */
/*                                                                       */
/*       Entry:  IOCC_Undefined(pIORB, npDCB)                            */
/*                                                                       */
/*               pIORB - ptr to the IORB.                                */
/*               npDCB - ptr to the DCB.                                 */
/*                                                                       */
/*       Exit:                                                           */
/*                                                                       */
/*************************************************************************/

VOID IOCC_Undefined(PIORB pIORB, NPDCB npDCB)
{
   pIORB->ErrorCode = IOERR_CMD_NOT_SUPPORTED;
   FinishIORB(pIORB,npDCB);
}
