/*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 = SCXFRSCB.C
 *
 * DESCRIPTIVE NAME = OS2SCSI.DMD - OS/2 SCSI.SYS Emulation
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : IBM SCB to SCSI CDB Translation
 *
 *
 *
*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI
#include "os2.h"
#include "strat2.h"
#include "dhcalls.h"
#include "reqpkt.h"
#include "scb.h"
#include "iorb.h"
#include "scsi.h"
#include "scscsi.h"
#include "scgen.h"
#include "abios.h"
#include "scproto.h"



extern UNITCB     UnitCB[1];               /* First UnitCB allocated here     */
extern USHORT     NumUnitCBs;              /* number of unit control blocks   */
extern NPSELARRAY npSelArray;              /* Near ptr to GDT selector array  */
extern UCHAR      CDB_cCode_to_cLen[];     /* Maps CDB code to CDB len.       */
extern TSB        TSB_GetCmdCmp;


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  XferSCB                                                 *
*                                                                           *
* DESCRIPTIVE NAME: Transfer SCB                                            *
*                                                                           *
* FUNCTION:         This routine handles Transfer SCB Commnad in the General*
*                   I/O request packet.  If the unit supports SCB, it sends *
*                   IORB with SCB, AND If the unit supports does not support*
*                   SCB, it sends IORB with DASD command or CDB.            *
*                                                                           *
* ENTRY POINT:      XferSCB                                                 *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            pGenioctl     Pointer to IOCTL packet                   *
*                                                                           *
* EXIT-NORMAL:      Status                                                  *
*                                                                           *
* EXIT-ERROR:       Status                                                  *
*                                                                           *
* Notes:            Called at TASK TIME                                     *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

USHORT near XferSCB(pGenioctl)

PRP_GENIOCTL        pGenioctl;
{
  PSCSI_IN_XFERSCB    pParm = (PSCSI_IN_XFERSCB) pGenioctl->ParmPacket;
  PSCSI_OUT           pData = (PSCSI_OUT)        pGenioctl->DataPacket;
  BOOL                rc;
  NPIORB              npIORB;
  NPUCB               npUCB;


  npUCB  = &UnitCB[pParm->hDev];

  DISABLE

  ENABLE

  npIORB = &npUCB->XferSCB_IORB;

  npUCB->pCurrentSCBH   = pParm->lpSCBH;
  npUCB->ppCurrentSCB   = pParm->ppSCB;
  npUCB->lnSenseData    = pParm->lnSenseData;
  npUCB->pSenseData     = (PSCSI_REQSENSE_DATA) pData;

  if (npUCB->IntUnitFlags & IUF_IBM_SCB)       /* SCB support unit */
  {
     if (!(rc = SendSCB( npUCB )))
     {
        (*(npUCB->AdapterDriverEP))((PIORB)(&npUCB->XferSCB_IORB));

        DISABLE;
        while ( !(npIORB->Status & IORB_DONE) )
        {
           DevHelp_ProcBlock ((ULONG)(PIORB) &npUCB->XferSCB_IORB, -1L, 0);
           DISABLE;
        }
        ENABLE;
     }
  }

  /*-------------------------------*/
  /* We are emulating SCB support  */
  /*-------------------------------*/
  else
  {
     /*-------------------------------------------------------*/
     /* A "GET COMMAND COMPLETE" SCB does not require an      */
     /* IORB to be passed to the ADD. In this case we         */
     /* continue processing the SCB chain rather than waiting */
     /* for a notification.                                   */
     /*-------------------------------------------------------*/
     do
     {
        rc = 0;

        if ( npUCB->pCurrentSCBH )
        {
           rc = DistributeCommand( npUCB );

           if(!rc)
           {
              (*(npUCB->AdapterDriverEP))((PIORB) &npUCB->XferSCB_IORB);
           }
        }
     }
     while (rc & REQ_CMDCMPST);

     /*-------------------------------------------------------*/
     /* Wait for the chain to complete if necessary.          */
     /*-------------------------------------------------------*/

     DISABLE;
     while ( !(npIORB->Status & IORB_DONE) )
     {
        DevHelp_ProcBlock ((ULONG)(PIORB) &npUCB->XferSCB_IORB, -1L, 0);
        DISABLE;
     }
     ENABLE;

  }

  return( CheckIORBError(npUCB, npIORB) );
}



#define CDB_CODE_GROUP_MASK 0xE0    /* Mask high 3 bits of CDB command code. */

USHORT near Get_CDB_Len(UCHAR CommandCode)
{
  return(CDB_cCode_to_cLen[ (CommandCode & CDB_CODE_GROUP_MASK) >> 5 ]);
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  DistributeCommand                                       *
*                                                                           *
* DESCRIPTIVE NAME: Distribute Command in SCB                               *
*                                                                           *
* FUNCTION:         This routine checks Command in SCB and call the         *
*                   appropriate routine to create IORB.                     *
*                   It is never called by IBM_SCB support unit.             *
*                                                                           *
* ENTRY POINT:      DistributeCommnad                                       *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                   npIORB        near pointer to allocated IORB            *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near DistributeCommand( npUCB )

NPUCB            npUCB;
{
  USHORT              CmdCode;
  USHORT              CmdMod;
  BOOL                rc;
  PSCB                pSCB;
  USHORT              CDBLength;

  pSCB = (PSCB)(npUCB->pCurrentSCBH+1);

  switch ((pSCB->Cmd)&(~SCB_ND_NS))
  {

     case (SCBREAD):
        /*****************************************/
        /* Read Data                             */
        /*****************************************/

        CmdCode = IOCC_EXECUTE_IO;
        CmdMod  = IOCM_READ;
        rc = SendExecuteIO(npUCB, CmdCode, CmdMod);

        break;

     case (SCBWRITE):
        /*****************************************/
        /* Write Data                            */
        /*****************************************/

        CmdCode = IOCC_EXECUTE_IO;
        CmdMod  = IOCM_WRITE;
        rc = SendExecuteIO(npUCB, CmdCode, CmdMod);

        break;

     case (SCBREADV):
        /*****************************************/
        /* Read Verify                           */
        /*****************************************/

        CmdCode = IOCC_EXECUTE_IO;
        CmdMod  = IOCM_READ_VERIFY;
        rc = SendExecuteIO(npUCB, CmdCode, CmdMod);

        break;

     case (SCBWRITEV):
        /*****************************************/
        /* Write Verify                          */
        /*****************************************/

        CmdCode = IOCC_EXECUTE_IO;
        CmdMod  = IOCM_WRITE_VERIFY;
        rc = SendExecuteIO(npUCB, CmdCode, CmdMod);

        break;

     case (SCBCMDSTATUS):
        /*****************************************/
        /* Get Command Complete Status           */
        /*****************************************/

        rc = GetCmdCmpStatus(npUCB);

        break;

     case (SCBCMDSENSE):
        /*****************************************/
        /* Request Sense                         */
        /*****************************************/

        rc = MakeReqSenseCDB(npUCB);

        break;

     case (SCBDEVICECAP):
        /*****************************************/
        /* Read Device Capacity                  */
        /*****************************************/

        rc = MakeReadDevCapCDB(npUCB);

        break;

     case (SCBDEVICEINQ):
        /*****************************************/
        /* Device Inquiry                        */
        /*****************************************/

        rc = MakeDevInquiryCDB(npUCB);

        break;

     case (SCBREASSIGNBLK):
        /*****************************************/
        /* Reassign Block                        */
        /*****************************************/

        rc = MakeReassignBlockCDB(npUCB);

        break;

     case (SCBSENDOTHER):
        /*****************************************/
        /* Send Other SCSI                       */
        /*****************************************/


        /*----------------------------------------------*/
        /* The device class driver should have set the  */
        /* CDB length in the LSB of the SCB LBA field.  */
        /*                                              */
        /* For drivers dont do this, attempt to         */
        /* calculate the CDB length from the CDB        */
        /* opcode.                                      */
        /*----------------------------------------------*/

        if ( !(CDBLength = (UCHAR) pSCB->LBA) )                      /*@V55100*/
        {
           CDBLength = (UCHAR) Get_CDB_Len( pSCB->EXT.CDB.SCSIcdb[0] );
        }

        rc = SendOtherSCSI( (NPUCB)  npUCB,
                            (PBYTE)  pSCB->EXT.CDB.SCSIcdb,
                            (USHORT) CDBLength               );
        break;

     case (SCBPREFETCH):
        /*****************************************/
        /* Read Prefetch                         */
        /*****************************************/

        CmdCode = IOCC_EXECUTE_IO;
        CmdMod  = IOCM_READ_PREFETCH;
        rc = SendExecuteIO(npUCB, CmdCode, CmdMod);

        break;

     case (SCBFORMATUNIT):
        /*****************************************/
        /* Format Unit                           */
        /*****************************************/

        rc = SendFormat(npUCB);

        break;

     default:
        rc = 1;
  }

  /*---------------------------------------------------------*/
  /* If a     SCB command is detected, jam a dummy error     */
  /* code into the IORB ErrorCode field so we have something */
  /* to process.                                             */
  /*---------------------------------------------------------*/

  if ( rc && !(rc & REQ_CMDCMPST) )
  {
     npUCB->XferSCB_IORB.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
     npUCB->XferSCB_IORB.Status    = IORB_DONE | IORB_ERROR;
  }

  return(rc);

}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  SendOtherSCSI                                           *
*                                                                           *
* DESCRIPTIVE NAME: Create Send Other SCSI Commnad IORB                     *
*                                                                           *
* FUNCTION:         This routine creates Send Other SCSI Commnad IORB.      *
*                                                                           *
* ENTRY POINT:      SendOtherSCSI                                           *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                   pCDB          Pointer to CDB already made               *
*                   npIORB        near pointer to allocated IORB            *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near SendOtherSCSI(npUCB, pCDB, lnCmd)
NPUCB                     npUCB;
PBYTE                     pCDB;
USHORT                    lnCmd;
{

  UCHAR                     oCode;
  NPIORB_ADAPTER_PASSTHRU   npIORBPT;
  NPIORB                    npIORB;
  NPSCSI_STATUS_BLOCK       npSSB;
  SEL                       sel;
  PSCB                      pSCB;
  USHORT                    i;
  ULONG                     lBuf;
  PSCATGATENTRY             pList;


  npIORBPT = (NPIORB_ADAPTER_PASSTHRU) (npIORB = &npUCB->XferSCB_IORB);

  CLEAR_IORB_BUFF;
  npSSB  = &npUCB->XferSCB_SSB;
  CLEAR_SSB_BUFF;

  npIORB->Length                = sizeof(IORB_ADAPTER_PASSTHRU) ;
  npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode           = IOCC_ADAPTER_PASSTHRU;
  npIORB->CommandModifier       = IOCM_EXECUTE_CDB;
  npIORB->RequestControl       |= (IORB_ASYNC_POST + IORB_REQ_STATUSBLOCK);
  npIORB->Timeout               = npUCB->Timeout;
  npIORB->StatusBlockLen        = sizeof(SCSI_STATUS_BLOCK);
  npIORB->pStatusBlock          = (NPBYTE)npSSB;
  npIORB->NotifyAddress         = GetNotification;
  *((NPUCB *)(npIORB->DMWorkSpace)) = npUCB;

  npSSB->ReqSenseLen = npUCB->lnSenseData;
  npSSB->SenseData   = npUCB->pSenseData;

  npIORBPT->ControllerCmdLen            = lnCmd;
  npIORBPT->pControllerCmd              = pCDB;

  pSCB = (PSCB)(npUCB->pCurrentSCBH+1);

  if(pSCB->ppXferBuf)
  {
     if ((pSCB->Enable & RD_SCB))
        npIORBPT->Flags    |= PT_DIRECTION_IN;

     if (!(pSCB->Enable & PT_SCB))
     {
        npIORBPT->cSGList   = (pSCB->XferBufLen) ? 1 : 0;            /*@V53750*/
        npIORBPT->pSGList   = (PSCATGATENTRY)(&(pSCB->ppXferBuf));
        npIORBPT->ppSGLIST  = npUCB->ppCurrentSCB + 8;

        if((((pSCB->Cmd)&(~SCB_ND_NS)) == SCBDEVICEINQ ) ||
                            (((pSCB->Cmd)&(~SCB_ND_NS)) == SCBCMDSENSE ))
           ((PSCSICDB6)pCDB)->XferLen   = (UCHAR)(pSCB->XferBufLen);
     }
     else
     {
        sel                                 = npSelArray->GDTSel[npUCB->Index];
        npIORBPT->cSGList                   = pSCB->XferBufLen / sizeof(SCATGATENTRY);

        if(DevHelp_PhysToGDTSelector((ULONG)  pSCB->ppXferBuf,
                                     (USHORT) pSCB->XferBufLen,
                                     (SEL)    sel))
           return(1);

        OFFSETOF(npIORBPT->pSGList)           = 0;
        SELECTOROF(npIORBPT->pSGList)         = sel;
        npIORBPT->ppSGLIST                    = pSCB->ppXferBuf;

        if((((pSCB->Cmd)&(~SCB_ND_NS)) == SCBDEVICEINQ ) ||
                            (((pSCB->Cmd)&(~SCB_ND_NS)) == SCBCMDSENSE ))
        {
           for (i = 0, lBuf = 0, pList = npIORBPT->pSGList;
                i < npIORBPT->cSGList;
                i++, pList++                                  )
              lBuf += pList->XferBufLen;

           ((PSCSICDB6)pCDB)->XferLen   = (UCHAR)lBuf;
        }
     }
  }

  return(0);
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  SendSCB                                                 *
*                                                                           *
* DESCRIPTIVE NAME: Prepare IORB including SCB                              *
*                                                                           *
* FUNCTION:         This routine creates IORB including Path through command*
*                   with SCB.                                               *
*                                                                           *
* ENTRY POINT:      SendSCB                                                 *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK TASK                                     *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near SendSCB(npUCB)

NPUCB                    npUCB;
{

  NPIORB_ADAPTER_PASSTHRU  npIORBPT;
  NPIORB                   npIORB;
  NPSCSI_STATUS_BLOCK      npSSB;
  USHORT                   i;

  npIORBPT = (NPIORB_ADAPTER_PASSTHRU)(npIORB = &npUCB->XferSCB_IORB);
  CLEAR_IORB_BUFF;
  npSSB  = &npUCB->XferSCB_SSB;
  CLEAR_SSB_BUFF;

  npIORB->Length                = sizeof(IORB_ADAPTER_PASSTHRU) ;
  npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode           = IOCC_ADAPTER_PASSTHRU;
  npIORB->CommandModifier       = IOCM_EXECUTE_SCB;
  npIORB->RequestControl       |= (IORB_ASYNC_POST + IORB_REQ_STATUSBLOCK);
  npIORB->Timeout               = npUCB->Timeout;
  npIORB->StatusBlockLen        = sizeof(SCSI_STATUS_BLOCK);
  npIORB->pStatusBlock          = (NPBYTE)npSSB;
  npIORB->NotifyAddress         = GetNotification;
  *((NPUCB *)(npIORB->DMWorkSpace)) = npUCB;

  npSSB->ReqSenseLen = npUCB->lnSenseData;
  npSSB->SenseData   = npUCB->pSenseData;


  npIORBPT->pControllerCmd              = (PBYTE)(npUCB->pCurrentSCBH);
  npIORBPT->ControllerCmdLen            = sizeof(SCBHDR) + sizeof(SCB);
  npIORBPT->ppSCB                       = npUCB->ppCurrentSCB;
                                /* S/G list paramters aren't used */

  return(0);
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  SendExecuteIO                                           *
*                                                                           *
* DESCRIPTIVE NAME: Prepare execute I/O IORB                                *
*                                                                           *
* FUNCTION:         This routine creates IORB including DASD commnad execute*
*                   I/O request.                                            *
*                                                                           *
* ENTRY POINT:      SendExecuteIO                                           *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                   CmdCode       Command code                              *
*                   CmdMod        Commnad modifier                          *
*                   npIORB        near pointer to allocated IORB            *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near SendExecuteIO(npUCB, CmdCode, CmdMod)

NPUCB               npUCB;
USHORT              CmdCode;
USHORT              CmdMod;
{
  PSCB                  pSCB;
  PSCATGATENTRY         p_SGList;
  ULONG                 pp_SGList;
  NPIORB_EXECUTEIO      npIORBEX;
  NPIORB                npIORB;
  NPSCSI_STATUS_BLOCK   npSSB;
  SEL                   sel;
  USHORT                i;

  npIORBEX = (NPIORB_EXECUTEIO)(npIORB = &npUCB->XferSCB_IORB);
  CLEAR_IORB_BUFF;
  npSSB  = &npUCB->XferSCB_SSB;
  CLEAR_SSB_BUFF;

  pSCB = (PSCB)(npUCB->pCurrentSCBH+1);

  npIORB->Length                = sizeof(IORB_EXECUTEIO);
  npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode           = CmdCode;
  npIORB->CommandModifier       = CmdMod;
  npIORB->RequestControl       |= (IORB_ASYNC_POST + IORB_REQ_STATUSBLOCK);
  npIORB->Timeout               = npUCB->Timeout;
  npIORB->StatusBlockLen        = sizeof(SCSI_STATUS_BLOCK);
  npIORB->pStatusBlock          = (NPBYTE)npSSB;
  npIORB->NotifyAddress         = GetNotification;
  *((NPUCB *)(npIORB->DMWorkSpace)) = npUCB;

  npSSB->ReqSenseLen = npUCB->lnSenseData;
  npSSB->SenseData   = npUCB->pSenseData;

  npIORBEX->RBA                         = pSCB->LBA;
  npIORBEX->BlockCount                  = pSCB->EXT.BLK.BlockCnt;
  npIORBEX->BlocksXferred               = 0;
  npIORBEX->BlockSize                   = pSCB->EXT.BLK.BlockSize;

  if ((((pSCB->Cmd)&(~SCB_ND_NS)) != SCBREADV) && (((pSCB->Cmd)&(~SCB_ND_NS)) != SCBPREFETCH))
  {
     if (!(pSCB->Enable & PT_SCB))
     {
        npIORBEX->cSGList   = (pSCB->XferBufLen) ? 1 : 0;            /*@V53750*/
        npIORBEX->pSGList   = (PSCATGATENTRY)(&(pSCB->ppXferBuf));
        npIORBEX->ppSGList  = npUCB->ppCurrentSCB + 8;
     }
     else
     {
        sel                          = npSelArray->GDTSel[npUCB->Index];
        npIORBEX->cSGList            = pSCB->XferBufLen / sizeof(SCATGATENTRY);

        if(DevHelp_PhysToGDTSelector((ULONG)  pSCB->ppXferBuf,
                                     (USHORT) pSCB->XferBufLen,
                                     (SEL)    sel)                 )
           return(1);

        OFFSETOF(npIORBEX->pSGList)    = 0;
        SELECTOROF(npIORBEX->pSGList)  = sel;
        npIORBEX->ppSGList             = pSCB->ppXferBuf;
     }
  }
  return(0);

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  SendFormat                                              *
*                                                                           *
* DESCRIPTIVE NAME: Prepare format IORB                                     *
*                                                                           *
* FUNCTION:         This routine creates IORB including DASD commnad format *
*                   I/O request.                                            *
*                                                                           *
* ENTRY POINT:      SendFormat                                              *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                   CmdCode       Command code                              *
*                   CmdMod        Commnad modifier                          *
*                   npIORB        near pointer to allocated IORB            *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near SendFormat(npUCB)

NPUCB               npUCB;
{
  PSCBFORMAT            pSCB;
  PSCATGATENTRY         p_SGList;
  ULONG                 pp_SGList;
  NPIORB_FORMAT         npIORBFT;
  NPIORB                npIORB;
  NPSCSI_STATUS_BLOCK   npSSB;
  NPSCSICDB6            npCDB;
  SEL                   sel;
  USHORT                i;

  npIORBFT = (NPIORB_FORMAT)(npIORB = &npUCB->XferSCB_IORB);
  CLEAR_IORB_BUFF;
  npSSB  = &npUCB->XferSCB_SSB;
  CLEAR_SSB_BUFF;
  npCDB  = (NPSCSICDB6)npUCB->CDB_Buff;
  CLEAR_CDB_BUFF;

  pSCB = (PSCBFORMAT)(npUCB->pCurrentSCBH+1);

  npIORB->Length                = sizeof(IORB_FORMAT);
  npIORB->UnitHandle            = npUCB->UnitInfo.UnitHandle;
  npIORB->CommandCode           = IOCC_FORMAT;
  npIORB->CommandModifier       = IOCM_FORMAT_MEDIA;
  npIORB->RequestControl       |= (IORB_ASYNC_POST + IORB_REQ_STATUSBLOCK);
  npIORB->Timeout               = npUCB->Timeout;
  npIORB->StatusBlockLen        = sizeof(SCSI_STATUS_BLOCK);
  npIORB->pStatusBlock          = (NPBYTE)npSSB;
  npIORB->NotifyAddress         = GetNotification;
  *((NPUCB *)(npIORB->DMWorkSpace)) = npUCB;

  npSSB->ReqSenseLen = npUCB->lnSenseData;
  npSSB->SenseData   = npUCB->pSenseData;
                                            /* Make Format Unit CDB */
  npCDB->Opcode                  = SCSI_FORMAT_UNIT;
  npCDB->Lun_MsbLBA              = npUCB->UnitInfo.UnitSCSILUN << 5;
  npCDB->Lun_MsbLBA             |= *((PUCHAR)(&pSCB->ModBits));
  npCDB->Lun_MsbLBA             |= DEFECT_BLOCK_FORMAT;
  *((PUSHORT)(&(npCDB->LsbLBA))) = pSCB->Interleave;

  if (!(pSCB->Enable & PT_SCB))
  {
     npIORBFT->cSGList   = (pSCB->XferBufLen) ? 1 : 0;               /*@V53750*/
     npIORBFT->pSGList   = (PSCATGATENTRY)(&(pSCB->ppXferBuf));
     npIORBFT->ppSGList  = npUCB->ppCurrentSCB + 8;
  }
  else
  {
     sel                              = npSelArray->GDTSel[npUCB->Index];
     npIORBFT->cSGList                = pSCB->XferBufLen / sizeof(SCATGATENTRY);

     if(DevHelp_PhysToGDTSelector((ULONG)  pSCB->ppXferBuf,
                                  (USHORT) pSCB->XferBufLen,
                                  (SEL)    sel               ) )
        return(1);

     OFFSETOF(npIORBFT->pSGList)   = 0;
     SELECTOROF(npIORBFT->pSGList) = sel;
     npIORBFT->ppSGList            = pSCB->ppXferBuf;
  }

  npIORBFT->FormatCmdLen           = sizeof(SCSICDB6);
  npIORBFT->pFormatCmd             = (PBYTE)npCDB;

  return(0);

}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  GetCmdCmpStatus                                         *
*                                                                           *
* DESCRIPTIVE NAME: Get Commnad complete status                             *
*                                                                           *
* FUNCTION:         This routine handles Get Command Complete Status in SCB.*
*                   About this commnad, OS2SCSI does not have to call to    *
*                   ADD, and if the unit supports IBM_SCB, OS2SCSI will     *
*                   return the last SCB saved, and if the unit does not     *
*                   support IBM_SCB, OS2SCSI will creates using the last    *
*                   error code and sense data saved.                        *
*                                                                           *
* ENTRY POINT:      GetCmdCmpStatus                                         *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near GetCmdCmpStatus(npUCB)

NPUCB               npUCB;
{
  PSCATGATENTRY         plist;
  PBYTE                 pdst, psrc;
  USHORT                ModeFlag;
  USHORT                count;
  USHORT                i;
  USHORT                j;
  USHORT                k;
  USHORT                rc = REQ_CMDCMPST;
  TSB                   TempTSB;
  PSCB                  pSCB;
  PSCBHDR               pSCBH;
  NPIORB                npIORB;
  NPSCSI_STATUS_BLOCK   npSSB;

  npIORB = &npUCB->XferSCB_IORB;
  CLEAR_IORB_BUFF;
  npSSB  = &npUCB->XferSCB_SSB;
  CLEAR_SSB_BUFF;

  CreateTSB(npUCB, &TempTSB);

  pSCBH = npUCB->pCurrentSCBH;
  pSCB  = (PSCB)(pSCBH+1);

  psrc  = (PBYTE)(&TempTSB);

  if ( DevHelp_PhysToVirt((ULONG)   pSCB->ppXferBuf,
                          (USHORT)  pSCB->XferBufLen,
                          (PPVOID)  &plist,
                          (PUSHORT) &ModeFlag)         )
     rc = 1;
  else
  {
     if (!(pSCB->Enable & PT_SCB))
     {
        pdst = (PBYTE)plist;

        for(k=0; (k<sizeof(TSB))&&(k<(USHORT)(pSCB->XferBufLen)); k++)
           *pdst++ = *psrc++;
     }
     else
     {
        for (i=0, j=0; i<(pSCB->XferBufLen/sizeof(SCATGATENTRY)); plist++, i++)
        {
           if(DevHelp_PhysToVirt((ULONG)   plist->ppXferBuf,
                                 (USHORT)  plist->XferBufLen,
                                 (PPVOID)  &pdst,
                                 (PUSHORT) &ModeFlag)       )
           {
              rc = 1;
              break;
           }

           for(k=0; (j<sizeof(TSB))&&(k<plist->XferBufLen); k++, j++)
              *pdst++ = *psrc++;
        }
     }
     DevHelp_UnPhysToVirt(&ModeFlag);
  }

  if (!rc)
  {
     npUCB->XferSCB_IORB.Status = IORB_DONE;

     /*----------------------------------------------------*/
     /* Return a dummy good TSB for the GetCommandComplete */
     /* request if an unconditional TSB is requested.      */
     /*----------------------------------------------------*/

     if (pSCB->Enable & ES_SCB)
     {
        pSCBH->pTSB = &TSB_GetCmdCmp;
     }

     npUCB->ppCurrentSCB = pSCB->ppNxtSCB;
     npUCB->pCurrentSCBH = pSCBH->pNextSCBHdr;
  }

  return(rc);
}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  MakeReqSenseCDB                                         *
*                                                                           *
* DESCRIPTIVE NAME: Make CDB for Request Sense Code                         *
*                                                                           *
* FUNCTION:         This routine prepares CDB for Request Sense Commnad     *
*                                                                           *
* ENTRY POINT:      MakeReqSenseCDB                                         *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near MakeReqSenseCDB(npUCB)

NPUCB                    npUCB;
{
  NPSCSICDB6              npCDB;
  USHORT                  i;

  npCDB  = (NPSCSICDB6)npUCB->CDB_Buff;
  CLEAR_CDB_BUFF;

  npCDB->Opcode      = SCSI_REQUEST_SENSE;
  npCDB->Lun_MsbLBA  = npUCB->UnitInfo.UnitSCSILUN << 5;

  return(SendOtherSCSI(npUCB,(PBYTE) npCDB, sizeof(SCSICDB6)));

}

/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  MakeReadDevCapCDB                                       *
*                                                                           *
* DESCRIPTIVE NAME: Make CDB for Request Sense Code                         *
*                                                                           *
* FUNCTION:         This routine prepares CDB for Request Sense Commnad     *
*                                                                           *
* ENTRY POINT:      MakeReqSenseCDB                                         *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near MakeReadDevCapCDB(npUCB)

NPUCB                    npUCB;
{
  NPSCSICDB10             npCDB;
  USHORT                  i;

  npCDB = (NPSCSICDB10)(npUCB->CDB_Buff);
  CLEAR_CDB_BUFF;

  npCDB->Opcode          = SCSI_READ_CAPACITY;
  npCDB->Lun             = npUCB->UnitInfo.UnitSCSILUN << 5;

  return(SendOtherSCSI(npUCB,(PBYTE)npCDB, sizeof(SCSICDB10)));

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  MakeDevInquiryCDB                                       *
*                                                                           *
* DESCRIPTIVE NAME: Make CDB for Device Inquiry                             *
*                                                                           *
* FUNCTION:         This routine prepares CDB for Device Inquiry Command    *
*                                                                           *
* ENTRY POINT:      MakeDevInquiryCDB                                       *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near MakeDevInquiryCDB(npUCB)

NPUCB                    npUCB;
{
  NPSCSICDB6             npCDB;
  USHORT                 i;

  npCDB = (NPSCSICDB6) npUCB->CDB_Buff;
  CLEAR_CDB_BUFF;


  npCDB->Opcode        = SCSI_INQUIRY;
  npCDB->Lun_MsbLBA    = npUCB->UnitInfo.UnitSCSILUN << 5;

  return(SendOtherSCSI(npUCB, (PBYTE) npCDB, sizeof(SCSICDB6)));

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  MakeReassignBlockCDB                                    *
*                                                                           *
* DESCRIPTIVE NAME: Make CDB for Reassign Block                             *
*                                                                           *
* FUNCTION:         This routine prepares CDB for Reassign Block Command    *
*                                                                           *
* ENTRY POINT:      MakeReaasignBlockCDB                                    *
*                                                                           *
* LINKAGE:          Call Near                                               *
*                                                                           *
* INPUT:            npUCB         Unit Control Block                        *
*                   npIORB        near pointer to allocated IORB            *
*                                                                           *
* EXIT-NORMAL:      0                                                       *
*                                                                           *
* EXIT-ERROR:       1                                                       *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

BOOL near MakeReassignBlockCDB(npUCB)

NPUCB                    npUCB;
{
  NPSCSICDB6              npCDB;
  USHORT                  i;

  npCDB = (NPSCSICDB6)npUCB->CDB_Buff;
  CLEAR_CDB_BUFF;

  npCDB->Opcode        = SCSI_REASSIGN_BLOCKS;
  npCDB->Lun_MsbLBA    = npUCB->UnitInfo.UnitSCSILUN << 5;

  return(SendOtherSCSI(npUCB, (PBYTE)npCDB, sizeof(SCSICDB6)));

}


/********************** START OF SPECIFICATIONS *****************************
*                                                                           *
* SUBROUTINE NAME:  GetNotification                                         *
*                                                                           *
* DESCRIPTIVE NAME: Get notification by ADD                                 *
*                                                                           *
* FUNCTION:         This routine is notified by ADD when the request is     *
*                   completed.                                              *
*                                                                           *
*                                                                           *
*                                                                           *
*                                                                           *
* ENTRY POINT:      GetNotification                                         *
*                                                                           *
* LINKAGE:          Call Far                                                *
*                                                                           *
* INPUT:            pIORB         Pointer to IORB                           *
*                                                                           *
* EXIT-NORMAL:                                                              *
*                                                                           *
* EXIT-ERROR:                                                               *
*                                                                           *
* Notes:            Called at TASK/INTERRUPT TIME                           *
*                                                                           *
*********************** END OF SPECIFICATIONS *******************************/

void _loadds far GetNotification(pIORB)
PIORBH           pIORB;
{
  NPUCB                 npUCB;
  NPIORB                npIORB;
  PSCBHDR               pSCBH;
  PSCB                  pSCB;

  USHORT                AwakeCount;
  USHORT                rc;



  if ( !(pIORB->Status & IORB_DONE) )
    return;

  npIORB    = (NPIORB)pIORB;
  npUCB     = *((NPUCB *)(npIORB->DMWorkSpace));

  /*------------------------------------*/
  /* Save for info for GetCmdCmpStatus  */
  /*------------------------------------*/

  npUCB->LastStatus  = npIORB->Status;
  npUCB->LastErrCode = npIORB->ErrorCode;
  npUCB->LastSSB     = *((NPSCSI_STATUS_BLOCK)npIORB->pStatusBlock);
  npUCB->ppLastSCB   = npUCB->ppCurrentSCB;

  /*----------------------------------*/
  /* If we are emulating SCB support  */
  /*----------------------------------*/

  if ( !(npUCB->IntUnitFlags & IUF_IBM_SCB) )
  {
     pSCBH = npUCB->pCurrentSCBH;
     pSCB  = (PSCB) (pSCBH+1);

     /*----------------------------------------------------*/
     /* Build a TSB if the SCB required unconditional      */
     /* building of a TSB or an error occurred.            */
     /*----------------------------------------------------*/

     if ( (pSCB->Enable & ES_SCB) || (npIORB->Status & IORB_ERROR) )
     {
        f_CreateTSB(npUCB, pSCBH->pTSB );
     }

     /*----------------------------------------------------------*/
     /* Stop emulation of the SCB chain on ERROR or if an ABORT. */
     /* request has been received. Ready the TASK TIME thread    */
     /* to return status to the requestor.                       */
     /*----------------------------------------------------------*/

     if ((npIORB->Status & IORB_ERROR) ||
                                (npUCB->IntUnitFlags & IUF_REQ_ABORT) )
     {
        DevHelp_ProcRun((ULONG)(PIORB) &npUCB->XferSCB_IORB, &AwakeCount);
     }

     /*--------------------------------------------------------*/
     /* Otherwise, continue emulation of the SCB chain.        */
     /* If there are no further SCBs to emulate, then run      */
     /* the TASK TIME originator.                              */
     /*--------------------------------------------------------*/

     else
     {
        /*---------------------------*/
        /* Address next SCB in chain */
        /*---------------------------*/

        npUCB->ppCurrentSCB = pSCB->ppNxtSCB;
        npUCB->pCurrentSCBH = pSCBH->pNextSCBHdr;

        /*-------------------------------------------------------*/
        /* A "GET COMMAND COMPLETE" SCB does not require an      */
        /* IORB to be passed to the ADD. In this case we         */
        /* continue processing the SCB chain rather than waiting */
        /* for a notification.                                   */
        /*-------------------------------------------------------*/

        do
        {
           rc = 0;

           if ( npUCB->pCurrentSCBH )
           {
              rc = DistributeCommand(npUCB);

              if(!rc)
              {
                 (*(npUCB->AdapterDriverEP))((PIORB)(&npUCB->XferSCB_IORB));
              }
           }
        }
        while (rc & REQ_CMDCMPST);


        /*-------------------------------------------------------*/
        /* If we have reached the end of the chain or detected   */
        /* a syntactic error in the chain, ready the TASK TIME   */
        /* thread.                                               */
        /*-------------------------------------------------------*/

        if ( !npUCB->pCurrentSCBH || rc )
        {
           DevHelp_ProcRun((ULONG)(PIORB) &npUCB->XferSCB_IORB, &AwakeCount);
        }
     }
  }

  /*--------------------------------------------*/
  /* If we are not emulating an SCB chain, then */
  /* this notification means we are done.       */
  /*--------------------------------------------*/

  else
  {
     DevHelp_ProcRun((ULONG)(PIORB) &npUCB->XferSCB_IORB, &AwakeCount);
  }

  return;

}
