/*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.      */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = OPIORB.C
 *
 * DESCRIPTIVE NAME = IORB managment routines for OS/2 Optical Device Manager
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   Handles allocation, initialization, and freeing of
 *             I/O request blocks (IORBs) used to communicate with
 *             Adapter Device Drivers (ADDs).
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "oph.h"


/****************************************************************************
 *
 * FUNCTION NAME = AllocIORB
 *
 * DESCRIPTION   = Allocates an IORB.
 *
 *                 USHORT AllocIORB  (NPUNITCB pUnitCB, NPIORB *pIORB)
 *
 * INPUT         = pUnitCB           - UnitCB requesting allocation
 *                 pIORB             - returned pointer to IORB
 *
 * OUTPUT        = USHORT            = 0, IORB allocated
 *                                   ! 0 , IORB not allocated
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT  AllocIORB(pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   FAR *pIORB;

{
   USHORT rc = 0;

   PUSHFLAGS;
   DISABLE;                            /* Make sure interrupts are disabled */

   /*
   ** Try allocating the dedicated IORB for the requesting unit
   */
   if ( !(pUnitCB->Flags & UCF_IORB_ALLOCATED) )
   {
      pUnitCB->Flags |= UCF_IORB_ALLOCATED;
      *pIORB = pUnitCB->pDedicatedIORB;
   }
   else
   {
     /*
     ** Dedicated IORB already allocated, so get an IORB from the pool.
     */
     if (CB_FreeList != 0)
     {
       *pIORB = (NPIORBH) CB_FreeList;           /* Get IORB from free list */
       CB_FreeList = (NPBYTE) (*pIORB)->pNxtIORB; /* Update free list head  */
     }
     else
       rc = 1;
   }
   /*
   ** Zero fill IORB
   */
   if (rc == 0)
      f_ZeroCB((PBYTE)*pIORB, sizeof(IORB_CDB));

   POPFLAGS;
   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = AllocIORB_Wait
 *
 * DESCRIPTION   = Allocate an IORB, wait until one is available
 *
 *       Allocates an IORB from the Control Block pool and block if one
 *       is not available.
 *
 *       VOID AllocIORB_Wait  (NPUNITCB pUnitCB, NPIORB *pIORB)
 *
 * INPUT         = pUnitCB           - UnitCB requesting allocation
 *                 pIORB             - returned pointer to IORB
 * OUTPUT        =
 *                 VOID
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID  AllocIORB_Wait (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORBH  FAR *pIORB;

{
   USHORT Allocated = FALSE;

   PUSHFLAGS;
   DISABLE;

   /*
   ** If init time, then grab the init time iorb
   */
   if (CDFlags & CDF_INIT_TIME)
      *pIORB = (NPIORBH) InitTimeIORB;

   /*
   ** Try allocating the dedicated IORB for the requesting unit
   */
   else if ( !(pUnitCB->Flags & UCF_IORB_ALLOCATED) )
   {
      pUnitCB->Flags |= UCF_IORB_ALLOCATED;
      *pIORB = pUnitCB->pDedicatedIORB;
   }

   else
   {
      do
      {
         if (CB_FreeList != 0)             /* Allocate from free list       */
         {
            *pIORB = (NPIORBH) CB_FreeList;
            CB_FreeList = (NPBYTE) (*pIORB)->pNxtIORB;       /* Update free */
            Allocated = TRUE;                                /*   list head */
         }
         else                         /* else wait till control block free  */
         {
            PoolSem++;                /* Indicate at least 1 thread blocked */
            _asm { int 3 };
            DevHelp_ProcBlock((ULONG) ppDataSeg, (ULONG)-1, 0);
            DISABLE;
            PoolSem--;                /* Indicate at least 1 thread blocked */
         }
      } while (Allocated == FALSE);
   }

   ENABLE;
   /*
   ** Zero fill the IORB
   */
   f_ZeroCB((PBYTE)*pIORB, sizeof(IORB_CDB) );

   POPFLAGS;
}


/****************************************************************************
 *
 * FUNCTION NAME = FreeIORB
 *
 * DESCRIPTION   = Free an IORB.
 *
 *                 VOID FreeIORB  (NPUNITCB pUnitCB, NPIORB pIORB)
 *
 * INPUT         = pUnitCB           - UnitCB requesting deallocation
 *                 pIORB             - IORB to deallocate
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID  FreeIORB (pUnitCB, pIORB)

NPUNITCB    pUnitCB;
NPIORB_CDB  pIORB;

{
   USHORT AwakeCount;

   PUSHFLAGS;
   DISABLE;

   /*
   ** If the IORB being freed is the unit's dedicated IORB, then simply
   ** clear the UCF_IORB_ALLOCATED flag in the UnitCB, otherwise return
   ** the IORB to the free pool.
   */
   if (pIORB == (NPIORB_CDB) pUnitCB->pDedicatedIORB)
      pUnitCB->Flags &= ~UCF_IORB_ALLOCATED;
   else
   {
      pIORB->apt.iorbh.pNxtIORB = (NPIORBH) CB_FreeList;
      CB_FreeList = (NPBYTE) pIORB;
   }

   /*
   ** If anyone waiting on the pool, then wake them up
   */
   if (PoolSem)
   {
      DevHelp_ProcRun((ULONG)ppDataSeg, &AwakeCount);
   }

   POPFLAGS;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_DeviceControl
 *
 * DESCRIPTION   = Setup a Device Control IORB
 *
 *       This routine allocates and sets up a Device Control IORB command
 *
 *       BuildIORB_DeviceControl (NPUNITCB pUnitCB, USHORT command_modifier,
 *                                                  NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB          - pointer to UnitCB
 *                 command_modifier - IORB command modifier
 *                 pIORBOut         - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildIORB_DeviceControl (pUnitCB, command_modifier, pIORBOut)

NPUNITCB   pUnitCB;
USHORT     command_modifier;
NPIORB FAR *pIORBOut;

{
    NPIORB pIORB;

    AllocIORB_Wait(pUnitCB, (NPIORB FAR *) &pIORB);

    pIORB->Length = sizeof(IORB_DEVICE_CONTROL);
    pIORB->UnitHandle = pUnitCB->UnitInfo.UnitHandle;
    pIORB->CommandCode = IOCC_DEVICE_CONTROL;
    pIORB->CommandModifier = command_modifier;
    pIORB->RequestControl = IORB_ASYNC_POST;
    pIORB->NotifyAddress = &NotifyDoneIORB;

    *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_Read
 *
 * DESCRIPTION   = Setup IORB for a Read
 *
 *     BuildIORB_Read (NPUNITCB pUnitCB, ULONG LBA, ULONG transfer_length,
 *                       ULONG transfer_addr, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 LBA             - logical block address
 *                 block_length    - block_length
 *                 transfer_length - count of blocks to transfer
 *                 transfer_addr   - transfer address
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildIORB_Read (pUnitCB, LBA, block_length, transfer_length,
                                                 transfer_addr, pIORBOut,Verify)

NPUNITCB   pUnitCB;
ULONG      LBA;
USHORT     block_length;
USHORT     transfer_length;
ULONG      transfer_addr;
NPIORB_EXECUTEIO FAR *pIORBOut;
BOOL       Verify;
{
   NPIORB_EXECUTEIO pIORB;
   NPIORB_DMWORK pDMWork;

   AllocIORB_Wait(pUnitCB, (NPIORB FAR *) &pIORB);

   pDMWork = (NPIORB_DMWORK) &(pIORB->iorbh.DMWorkSpace);

   pIORB->iorbh.Length = sizeof(IORB_EXECUTEIO);
   pIORB->iorbh.UnitHandle = pUnitCB->UnitInfo.UnitHandle;
   pIORB->iorbh.CommandCode = IOCC_EXECUTE_IO;
   pIORB->iorbh.CommandModifier = Verify?IOCM_READ_VERIFY:IOCM_READ;
   pIORB->iorbh.RequestControl = IORB_ASYNC_POST | IORB_REQ_STATUSBLOCK;
   pIORB->iorbh.NotifyAddress = &NotifyDoneIORB;

   /*
   ** Setup the scatter/gather list
   */
   pIORB->cSGList = 1;
   pIORB->pSGList = (PVOID) pIORB;
   OFFSETOF(pIORB->pSGList) = (USHORT) ( &(pDMWork->SGList) );
   pIORB->ppSGList =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pDMWork->SGList)) );

   pDMWork->SGList.ppXferBuf = transfer_addr;
   pDMWork->SGList.XferBufLen = transfer_length * block_length;

   /*
   ** Set up the pointers to the status block and sense data block
   */

   /*
   ** Set up the rest of the EXECUTE_IO IORB
   */
   pIORB->RBA = LBA;
   pIORB->BlockCount = transfer_length;
   pIORB->BlockSize = block_length;

   *pIORBOut = pIORB;
}
/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_Write
 *
 * DESCRIPTION   = Setup IORB for a Write
 *
 *     BuildIORB_Write (NPUNITCB pUnitCB, ULONG LBA, ULONG transfer_length,
 *                       ULONG transfer_addr, NPIORB_CDB FAR *pIORBOut,BOOL Verify)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 LBA             - logical block address
 *                 block_length    - block_length
 *                 transfer_length - count of blocks to transfer
 *                 transfer_addr   - transfer address
 *                 pIORBOut        - returned pointer to IORB
 *                 Verify          - write with verify
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildIORB_Write (pUnitCB, LBA, block_length, transfer_length,
                                                 transfer_addr, pIORBOut,Verify)

NPUNITCB   pUnitCB;
ULONG      LBA;
USHORT     block_length;
USHORT     transfer_length;
ULONG      transfer_addr;
NPIORB_EXECUTEIO FAR *pIORBOut;
BOOL Verify;

{
   NPIORB_EXECUTEIO pIORB;
   NPIORB_DMWORK pDMWork;

   AllocIORB_Wait(pUnitCB, (NPIORB FAR *) &pIORB);

   pDMWork = (NPIORB_DMWORK) &(pIORB->iorbh.DMWorkSpace);

   pIORB->iorbh.Length = sizeof(IORB_EXECUTEIO);
   pIORB->iorbh.UnitHandle = pUnitCB->UnitInfo.UnitHandle;
   pIORB->iorbh.CommandCode = IOCC_EXECUTE_IO;
   pIORB->iorbh.CommandModifier = Verify?IOCM_WRITE_VERIFY:IOCM_WRITE;
   pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
   pIORB->iorbh.NotifyAddress = &NotifyDoneIORB;

   /*
   ** Setup the scatter/gather list
   */
   pIORB->cSGList = 1;
   pIORB->pSGList = (PVOID) pIORB;
   OFFSETOF(pIORB->pSGList) = (USHORT) ( &(pDMWork->SGList) );
   pIORB->ppSGList =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pDMWork->SGList)) );

   pDMWork->SGList.ppXferBuf = transfer_addr;
   pDMWork->SGList.XferBufLen = transfer_length * block_length;

   /*
   ** Set up the rest of the EXECUTE_IO IORB
   */
   pIORB->RBA = LBA;
   pIORB->BlockCount = transfer_length;
   pIORB->BlockSize = block_length;

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_PassthruCDB
 *
 * DESCRIPTION   = Setup a passthru IORB for a CDB
 *
 *    This routine allocates and sets up a CDB passthru IORB
 *
 *    BuildIORB_PassthruCDB (NPUNITCB pUnitCB, ULONG data_length, ULONG ppData,
 *                                                     NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 data_length     - length of data to transfer
 *                 ppData          - physical address of data buffer
 *                 pIORBOut        - pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildIORB_PassthruCDB (pUnitCB, data_length, ppData, pIORBOut)

NPUNITCB        pUnitCB;
ULONG           data_length;
ULONG           ppData;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB    pIORB;
   NPIORB_DMWORK pDMWork;


   AllocIORB_Wait(pUnitCB, (NPIORB FAR *) &pIORB);

   pDMWork = (NPIORB_DMWORK) &(pIORB->apt.iorbh.DMWorkSpace);

   *pIORB = default_iorb_cdb;

    pIORB->apt.iorbh.UnitHandle = pUnitCB->UnitInfo.UnitHandle;
    pIORB->apt.iorbh.NotifyAddress = &NotifyDoneIORB;

    /*
    ** The pointers to the scat/gather list are always set up even
    ** if no scatter/gather list is needed.  We use the scatter/gather
    ** count field to indicate if the list should be used.  This is
    ** done this way, because some commands require a scat/gat list
    ** in the filter, but not by the CD-ROM Device Manager.
    */
    pIORB->apt.pSGList = (PVOID) pIORB;
    OFFSETOF(pIORB->apt.pSGList) = (USHORT) ( &(pDMWork->SGList) );
    pIORB->apt.ppSGLIST =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pDMWork->SGList)) );

    pDMWork->SGList.ppXferBuf =
                 (ULONG)(ppDataSeg+(ULONG)((USHORT)&(pIORB->CDB_data)));

   /*
   ** If data transfer involved, set scatter/gather count to 1
   */
   if (data_length != 0)
   {
      pIORB->apt.cSGList = 1;
      pDMWork->SGList.XferBufLen = data_length;

      if (ppData != -1)
         pDMWork->SGList.ppXferBuf = ppData;
   }

   /*
   ** Fill in the pointer to the SCSI CDB
   */
   pIORB->apt.pControllerCmd = (PVOID) pIORB;
   OFFSETOF(pIORB->apt.pControllerCmd) = (USHORT) ( &(pIORB->CDB) );
   pIORB->apt.ppSCB =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pIORB->CDB)) );

   /*
   ** Set up the pointers to the status block and sense data block
   */
   (USHORT) pIORB->apt.iorbh.pStatusBlock = (USHORT) ( &(pIORB->status_block) );
   pIORB->apt.iorbh.StatusBlockLen = sizeof(SCSI_STATUS_BLOCK);

   pIORB->status_block.ReqSenseLen = sizeof(struct Sense_Data);
   pIORB->status_block.SenseData = (PVOID) pIORB;
   OFFSETOF(pIORB->status_block.SenseData) = (USHORT) (&(pIORB->sense_data));

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_TestUnitReady
 *
 * DESCRIPTION   = Setup CDB for Test Unit Ready
 *
 *        BuildCDB_TestUnitReady (NPUNITCB pUnitCB, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_TestUnitReady(pUnitCB, pIORBOut)

NPUNITCB pUnitCB;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB pIORB;

   struct CDB_TestUnitReady NEAR *pCDB;

  /*
  ** Build a CDB passthru IORB
  */
   BuildIORB_PassthruCDB(pUnitCB, 0, 0, (NPIORB_CDB FAR *) &pIORB);

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_TestUnitReady);

   pCDB = (struct CDB_TestUnitReady NEAR *) &(pIORB->CDB);
   pCDB->OpCode = SCSI_TEST_UNIT_READY;

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_RezeroUnit
 *
 * DESCRIPTION   = Setup CDB for Rezero Unit command
 *
 *        BuildCDB_RezeroUnit (NPUNITCB pUnitCB, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_RezeroUnit(pUnitCB, pIORBOut)

NPUNITCB pUnitCB;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB pIORB;

   struct CDB_RezeroUnit NEAR *pCDB;

  /*
  ** Build a CDB passthru IORB
  */
   BuildIORB_PassthruCDB(pUnitCB, 0, 0, (NPIORB_CDB FAR *) &pIORB);

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_RezeroUnit);

   pCDB = (struct CDB_RezeroUnit NEAR * ) &(pIORB->CDB);
   pCDB->OpCode = SCSI_REZERO_UNIT;

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_RequestSense
 *
 * DESCRIPTION   = Setup CDB for Request Sense Command
 *
 *        BuildCDB_RequestSense (NPUNITCB pUnitCB, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_RequestSense(pUnitCB, pIORBOut)

NPUNITCB pUnitCB;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB    pIORB;
   struct CDB_RequestSense NEAR *pCDB;

   /*
   ** Build a CDB passthru IORB
   */
   BuildIORB_PassthruCDB(pUnitCB,
                         sizeof(struct Sense_Data),
                         -1L,
                         (NPIORB_CDB FAR *) &pIORB);

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_RezeroUnit);
   pIORB->apt.Flags = PT_DIRECTION_IN;

   /*
   ** Setup CDB
   */
   pCDB = (struct CDB_RequestSense NEAR *) &(pIORB->CDB);
   pCDB->OpCode = SCSI_REQUEST_SENSE;
   pCDB->alloc_length = sizeof(struct Sense_Data);

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_Inquiry
 *
 * DESCRIPTION   = Setup SCSI Inquiry Command Descriptor Block
 *
 *         This routine builds a SCSI Inquiry CDB.
 *
 *         BuildCDB_Inquiry (NPUNITCB pUnitCB, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_Inquiry(pUnitCB, pIORBOut)

NPUNITCB    pUnitCB;
NPIORB_CDB  FAR *pIORBOut;                     /* ptr to IORB              */
{
  NPIORB_CDB  pIORB;                           /* ptr to IORB              */

  struct
  CDB_Inquiry *pCDB;                           /* ptr to CDB               */

  /*
  ** Build a CDB passthru IORB
  */
  BuildIORB_PassthruCDB (pUnitCB,
               sizeof(struct Inquiry_Data),
               (ULONG) (ppDataSeg+(ULONG)((USHORT)&(pUnitCB->InquiryData))),
               (NPIORB_CDB FAR *) &pIORB );

  pIORB->apt.ControllerCmdLen = sizeof(struct CDB_Inquiry);
  pIORB->apt.Flags |= PT_DIRECTION_IN;

  /*
  ** Setup the CDB
  */
  pCDB = (struct CDB_Inquiry NEAR *) &pIORB->CDB;
  pCDB->OpCode = SCSI_INQUIRY;
  pCDB->alloc_length = sizeof(struct Inquiry_Data);

  *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_ModeSelect
 *
 * DESCRIPTION   = Setup CDB for Mode Select command
 *
 *                 Note: This routine should not be called by an ATAPI
 *                       device since the 6 byte command is not supported.
 *
 *      BuildCDB_ModeSelect (NPUNITCB pUnitCB, USHORT density_code,
 *                           USHORT block_length, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 density_code    - density code
 *                 block_length    - block_length
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_ModeSelect(pUnitCB, type, pIORBOut)

NPUNITCB   pUnitCB;
USHORT     type;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB pIORB;
   USHORT parm_length,page_size=0;

   struct CDB_ModeSelect  NEAR *pCDB;
   struct CDB_ModeSelect_10  NEAR *pCDB10;
   MODEPAGES NEAR *pDescriptor;

   parm_length = sizeof(struct ModeSelectHdr);
   switch(type)
     {
     case PAGE_0:
       parm_length+=page_size=sizeof(struct ModeSelectPage0);
       break;
     case PAGE_20:
       parm_length+=page_size=sizeof(struct ModeSelectPage20);
       break;
     case PAGE_21:
       parm_length+=page_size=sizeof(struct ModeSelectPage21);
       break;
     default:
       break;
     } /* endswitch */

  /*
  ** Build a CDB passthru IORB
  */
   BuildIORB_PassthruCDB (pUnitCB,
                          parm_length,
                          -1L,
                         (NPIORB_CDB FAR *) &pIORB );

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_ModeSelect);

   /*
   ** Setup the CDB
   */
   if ( pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)
     {
     pCDB10 = (struct CDB_ModeSelect_10 NEAR *) &(pIORB->CDB);
     pCDB10->OpCode = SCSI_MODE_SELECT_10;
     pCDB10->PF = 1;
     pCDB10->parm_length.usbytes.byte_1 = page_size;
     } /* endif */
   else
     {
     pCDB = (struct CDB_ModeSelect NEAR *) &(pIORB->CDB);
     pCDB->OpCode = SCSI_MODE_SELECT;
     pCDB->PF = 1;
     pCDB->parm_length = page_size;
     } /* endelse */

   /*
   ** Set up the Mode Parameter Header
   */
   pDescriptor = (MODEPAGES NEAR *) &(pIORB->CDB_data);
   pDescriptor->ModeSelectHdr.mode_data_length = 0;
   pDescriptor->ModeSelectHdr.medium_type = 0;
   pDescriptor->ModeSelectHdr.device_specific_parm = 0;
   pDescriptor->ModeSelectHdr.block_descriptor_len =0;

   pDescriptor->ModeSelectHdr.page_code=type;
   pDescriptor->ModeSelectHdr.page_length=page_size;

   switch(type)
     {
     case PAGE_0:
       pDescriptor->Page.Page0.select = AVRE_BIT;
       break;
     case PAGE_20:
       pDescriptor->Page.Page20.number_of_groups_msb = 0;
       pDescriptor->Page.Page20.number_of_groups_lsb = 16;
       pDescriptor->Page.Page20.blocks_per_group_msb = 0;
       pDescriptor->Page.Page20.blocks_per_group = 0x3C;
       pDescriptor->Page.Page20.blocks_per_group_lsb = 0xBF;
       pDescriptor->Page.Page20.spare_blocks_per_group_msb = 0;
       pDescriptor->Page.Page20.spare_blocks_per_group = 0;
       pDescriptor->Page.Page20.spare_blocks_per_group_lsb = 64;
       break;
     case PAGE_21:
       pDescriptor->Page.Page21.laser_timeout_value = (UCHAR) LaserTimeoutValue;
       break;
     default:
      break;
     } /* endswitch */

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_PreventAllowRemoval
 *
 * DESCRIPTION   = Setup CDB for Prevent Allow Media Removal
 *
 *                 BuildCDB_PreventAllowRemoval (NPUNITCB pUnitCB,  flag,
 *                                               NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 flag            - prevent-allow flag
 *                                   0 = allow medium removal
 *                                   1 = prevent medium removal
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_PreventAllowRemoval (pUnitCB, flag, pIORBOut)

NPUNITCB pUnitCB;
USHORT   flag;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB pIORB;

   struct CDB_PreventAllowRemoval NEAR *pCDB;

   /*
   ** Build a CDB passthru IORB
   */
   BuildIORB_PassthruCDB(pUnitCB, 0, 0, (NPIORB_CDB FAR *) &pIORB);

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_PreventAllowRemoval);

   /*
   ** Setup the CDB
   */
   pCDB = (struct CDB_PreventAllowRemoval NEAR *) &(pIORB->CDB);
   pCDB->OpCode = SCSI_LOCK_UNLOCK;

   pCDB->prevent = flag;

   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_ReadCapacity
 *
 * DESCRIPTION   = Setup CDB for Read Capacity Command
 *
 *      BuildCDB_ReadCapacity (NPUNITCB pUnitCB, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_ReadCapacity(pUnitCB, pIORBOut)

NPUNITCB        pUnitCB;
NPIORB_CDB FAR *pIORBOut;

{
  NPIORB_CDB pIORB;

  struct CDB_ReadCapacity *pCDB;               /* ptr to CDB               */

  /*
  ** Build a CDB passthru IORB
  */
  BuildIORB_PassthruCDB(pUnitCB,
                        sizeof(struct ReadCapacity_Data),
                        -1L,
                        (NPIORB_CDB FAR *) &pIORB );

  pIORB->apt.ControllerCmdLen = sizeof(struct CDB_ReadCapacity);
  pIORB->apt.Flags |= PT_DIRECTION_IN;

  /*
  ** Setup the CDB
  */
  pCDB = (struct CDB_ReadCapacity NEAR *) &pIORB->CDB;
  pCDB->OpCode = SCSI_READ_CAPACITY;
  pCDB->RelAdr = 0;
  pCDB->LBA.dword = 0;
  pCDB->PMI = 0;
  pCDB->control = 0;

  *pIORBOut = pIORB;

}



/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_StartStopUnit
 *
 * DESCRIPTION   = Setup CDB for Start Stop Unit command
 *
 *       BuildCDB_StartStopUnit (NPUNITCB pUnitCB,  flag,
 *                                               NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 flag            - start-stop flag
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_StartStopUnit (pUnitCB, type, pIORBOut)

NPUNITCB pUnitCB;
USHORT   type;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB pIORB;

   struct CDB_StartStopUnit NEAR *pCDB;

   /*
   ** Build a CDB passthru IORB
   */
   BuildIORB_PassthruCDB(pUnitCB, 0, 0, (NPIORB_CDB FAR *) &pIORB);

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_StartStopUnit);

   /*
   ** Setup the CDB
   */
   pCDB = (struct CDB_StartStopUnit NEAR *) &(pIORB->CDB);
   pCDB->OpCode = SCSI_START_STOP_UNIT;


   switch (type)
   {
      case CDBF_START:
         pCDB->start = 1;
         pCDB->LoEj = 0;
         break;

      case CDBF_EJECT:
         pCDB->start = 0;
         pCDB->LoEj = 1;
         break;

      case CDBF_CLOSE_TRAY:
         pCDB->start = 1;
         pCDB->LoEj = 1;
         break;
   }


   *pIORBOut = pIORB;
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_FormatUnit
 *
 * DESCRIPTION   = Setup CDB for Formayt Unit command
 *
 *       BuildCDB_FormatUnit (NPUNITCB pUnitCB,  flag,
 *                                               NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 flag            - start-stop flag
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildIORB_FormatUnit (pUnitCB, byte_1, VendorSpecific, ppDefect_list, defect_list_size, pIORBOut)

NPUNITCB pUnitCB;
BYTE    byte_1;
BYTE    VendorSpecific;
NPIORB_CDB FAR *pIORBOut;
ULONG  ppDefect_list;
USHORT defect_list_size;

{
   struct CDB_FormatUnit *pFCU;
   NPIORB_CDB pIORB;

  /*
  ** Build a CDB passthru IORB
  */

   BuildIORB_PassthruCDB(pUnitCB,
                        defect_list_size,
                        ppDefect_list,
                        (NPIORB_CDB FAR *) &pIORB );


   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_FormatUnit);

   pFCU = (struct CDB_FormatUnit *) &(pIORB->CDB);

   pFCU->OpCode = SCSI_FORMAT_UNIT;
   pFCU->VendorSpecific=VendorSpecific;
   pFCU->byte_1.byte|=byte_1;           // or in the fmtdata/cmplst/defectlistformat bits

   *pIORBOut = (NPIORB_CDB)pIORB;

}


/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_Read_10
 *
 * DESCRIPTION   = Setup CDB for Read (10) command
 *
 *      BuildCDB_Read_10 (NPUNITCB pUnitCB, ULONG LBA, ULONG transfer_length,
 *                        ULONG transfer_addr, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 LBA             - logical block address
 *                 block_length    - block_length
 *                 transfer_length - count of blocks to transfer
 *                 transfer_addr   - transfer address
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_Read_10 (pUnitCB, LBA, block_length, transfer_length,
                                                   transfer_addr, pIORBOut, Verify)

NPUNITCB   pUnitCB;
ULONG      LBA;
USHORT     block_length;
USHORT     transfer_length;
ULONG      transfer_addr;
NPIORB_CDB FAR *pIORBOut;
BOOL Verify;
{
   NPIORB_CDB pIORB;

   struct CDB_Read_10  NEAR *pCDB;

   union ULONGB  ul_LBA;
   union USHORTB us_transfer_length;

   ul_LBA.dword = LBA;
   us_transfer_length.word = transfer_length;

   /*
   ** Build a CDB passthru IORB
   */
   BuildIORB_PassthruCDB ( pUnitCB,
                           (ULONG) transfer_length * block_length,
                           transfer_addr,
                           (NPIORB_CDB FAR *) &pIORB );

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_Read_10);
   pIORB->apt.Flags = PT_DIRECTION_IN;

   /*
   ** Setup the CDB
   */
   pCDB = (struct CDB_Read_10 NEAR *) &(pIORB->CDB);
   pCDB->OpCode = Verify?SCSI_VERIFY_10: SCSI_READ_10;

   pCDB->LBA.ulbytes.byte_0 = ul_LBA.ulbytes.byte_3;
   pCDB->LBA.ulbytes.byte_1 = ul_LBA.ulbytes.byte_2;
   pCDB->LBA.ulbytes.byte_2 = ul_LBA.ulbytes.byte_1;
   pCDB->LBA.ulbytes.byte_3 = ul_LBA.ulbytes.byte_0;

   pCDB->transfer_length.usbytes.byte_0 = us_transfer_length.usbytes.byte_1;
   pCDB->transfer_length.usbytes.byte_1 = us_transfer_length.usbytes.byte_0;

   *pIORBOut = pIORB;
}
/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_Write_10
 *
 * DESCRIPTION   = Setup CDB for Write (10) command
 *
 *      BuildCDB_Read_10 (NPUNITCB pUnitCB, ULONG LBA, ULONG transfer_length,
 *                        ULONG transfer_addr, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 LBA             - logical block address
 *                 block_length    - block_length
 *                 transfer_length - count of blocks to transfer
 *                 transfer_addr   - transfer address
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_Write_10 (pUnitCB, LBA, block_length, transfer_length,
                                                   transfer_addr, pIORBOut,Verify)

NPUNITCB   pUnitCB;
ULONG      LBA;
USHORT     block_length;
USHORT     transfer_length;
ULONG      transfer_addr;
NPIORB_CDB FAR *pIORBOut;
BOOL Verify;
{
   NPIORB_CDB pIORB;

   struct CDB_Write_10  NEAR *pCDB;

   union ULONGB  ul_LBA;
   union USHORTB us_transfer_length;

   ul_LBA.dword = LBA;
   us_transfer_length.word = transfer_length;

   /*
   ** Build a CDB passthru IORB
   */
   BuildIORB_PassthruCDB ( pUnitCB,
                           (ULONG) transfer_length * block_length,
                           transfer_addr,
                           (NPIORB_CDB FAR *) &pIORB );

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_Read_10);
//   pIORB->apt.Flags = PT_DIRECTION_IN;

   /*
   ** Setup the CDB
   */
   pCDB = (struct CDB_Write_10 NEAR *) &(pIORB->CDB);
   pCDB->OpCode = SCSI_WRITE_10;

   pCDB->LBA.ulbytes.byte_0 = ul_LBA.ulbytes.byte_3;
   pCDB->LBA.ulbytes.byte_1 = ul_LBA.ulbytes.byte_2;
   pCDB->LBA.ulbytes.byte_2 = ul_LBA.ulbytes.byte_1;
   pCDB->LBA.ulbytes.byte_3 = ul_LBA.ulbytes.byte_0;

   pCDB->transfer_length.usbytes.byte_0 = us_transfer_length.usbytes.byte_1;
   pCDB->transfer_length.usbytes.byte_1 = us_transfer_length.usbytes.byte_0;

   *pIORBOut = pIORB;
}
/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_SenseCapabilities
 *
 * DESCRIPTION   = Build CDB for Mode Sense of Capabilities Page
 *
 *        Build a Mode Sense command for the CD-ROM Capabilities page.
 *
 *        BuildCDB_SenseCapabilities (NPUNITCB pUnitCB, NPIORB_CDB FAR *pIORBOut)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORBOut        - returned pointer to IORB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildCDB_GetLockStatus (pUnitCB, pIORBOut)

NPUNITCB   pUnitCB;
NPIORB_CDB FAR *pIORBOut;

{
   NPIORB_CDB pIORB;
   struct CDB_ModeSense_6  NEAR *pCDB;
   struct CDB_ModeSense_10 NEAR *pCDB10;

  /*
  ** Build a CDB passthru IORB
  */

   BuildIORB_PassthruCDB(pUnitCB,
                        sizeof(struct ModeSensePage0MaxSize),
                        -1L,
                        (NPIORB_CDB FAR *) &pIORB );

   pIORB->apt.ControllerCmdLen = sizeof(struct CDB_ModeSense_6);
   pIORB->apt.Flags |= PT_DIRECTION_IN;

   /*
   ** Setup the CDB
   */

   if ( pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI)
     {
     pCDB10 = (struct CDB_ModeSense_10 NEAR *) &(pIORB->CDB);
     pCDB10->OpCode = SCSI_MODE_SENSE_10;
     pCDB10->DBD = 0;
     pCDB10->page_code = 0;
     pCDB10->PC = 0;
     pCDB10->alloc_length.usbytes.byte_1 = sizeof(struct ModeSensePage0MaxSize);
     pCDB10->control = 0;
     } /* endif */
   else
     {
     pCDB = (struct CDB_ModeSense_6 NEAR *) &(pIORB->CDB);
     pCDB->OpCode = SCSI_MODE_SENSE;
     pCDB->DBD = 0;
     pCDB->page_code = 0;
     pCDB->PC = 0;
     pCDB->alloc_length = sizeof(struct ModeSensePage0MaxSize);
     pCDB->control = 0;
     } /* endelse */

   *pIORBOut = pIORB;

}
