/*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 = SCSIREQ3.C
 *
 * DESCRIPTIVE NAME = IBM2SCSI.ADD - Adapter Driver for IBM SCSI adapters.
 *		      IORB Processing.
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION	This file implements the following IORB processing routines:
 *
 *		IOCC_EXECUTEIO
 *		IOCC_DEVICECONTROL
 *
 *
*/

/*----------------------*/
/* 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_ExecuteIO							 */
/*									 */
/*    This is the main control routine for the IOCC Execure IO command.  */
/*    It is for locate mode.						 */
/*									 */
/*	 Entry:  IOCC_ExecuteIO(pIO, npDCB)				 */
/*									 */
/*		 pIO - pointer to the IORB				 */
/*		 npDCB - ptr to the DCB 				 */
/*									 */
/*	 Exit:	None.							 */
/*									 */
/*************************************************************************/

VOID IOCC_ExecuteIO(PIORB_EXECUTEIO pExecIO, NPDCB npDCB)

{

   USHORT rc;

   /*------------------------*/
   /* Call the setup routine */
   /*------------------------*/

   rc = IOCC_ExecIOBldSCBChain(pExecIO, npDCB);

   /*----------------------------------------------------------*/
   /* If the setup routine did not return NORESOURCE then part */
   /* of the request is ready to go.  If the device is IDLE    */
   /* start it.  If complete then finish it.  Otherwise Q it.  */
   /*----------------------------------------------------------*/

   SAVEIF();
   if (rc == COMPLETE) {
      FinishIORB((PIORB)pExecIO, npDCB);
   }
   else if (rc != NORESOURCE && npDCB->state == IDLE) {
      StartIORB((PIORB)pExecIO, npDCB);
   }
   else {
      QueueIORB((PIORB)pExecIO, npDCB, BACK);		 /* queue it */
   }
   RESTOREIF();
}


/*************************************************************************/
/*									 */
/* IOCC_ExecIOBldSCBChain						 */
/*									 */
/*    This routine builds an SCB chain for the IORB.  It will only build */
/*    a chain of the current max scbs/request limit.  If entered at	 */
/*    interrupt time when the request has been partially completed then  */
/*    the SCBs are re-used before allocating new ones.	If at the end	 */
/*    of building the SCB chain ther are some left over they are freed.  */
/*    This insures that there are SCBs for use so the I/O can continue.  */
/*									 */
/*	 Entry:  IOCC_ExecIOBldSCBChain(pIO, npDCB)			 */
/*									 */
/*		 pIO - pointer to the IORB				 */
/*		 npDCB - ptr to the DCB 				 */
/*									 */
/*	 Exit: NORESOURCE - unable to allocate any resource		 */
/*	       INCOMPLETE - able to partially allocate resources	 */
/*	       READY	  - request is ready completely 		 */
/*									 */
/*	 Notes: This routine call be called at interrupt time.		 */
/*		This routine is a call-out routine.			 */
/*									 */
/*************************************************************************/


USHORT IOCC_ExecIOBldSCBChain(PIORB_EXECUTEIO pIO, NPDCB npDCB)

{

   ULONG	  ppCurSGList;	   /* current phys addr of SG list	     */
   ULONG	  CurRBA;	   /* current RBA for the current SCB	     */
   USHORT	  cCurSGList;	   /* count of SG entries in this SCB	     */
   USHORT	  cBlks;	   /* count of blocks in the SG list	     */
   USHORT	  cTotSCBs;	   /* count of total # of SCBs needed	     */
   USHORT	  cSGLeft;	   /* count of SG entries left		     */
   USHORT	  i;		   /* loop var				     */
   USHORT	  cCurSGs ;	   /* cnt of SG grps of 16 done before call  */
   USHORT	  type; 	   /* type of IO (aligned with SCB command)  */
   USHORT	  rc;		   /* return code to return		     */
   NPSCBH	  npSCBH;	   /* ptr to SCB control area		     */
   NPSCBH	  npSCBListH;	   /* ptr to SCB list if request is partial  */
   NPSCBH	  npLastSCBH;	   /* ptr to last SCB ctrl area in chain     */
   PSCATGATENTRY  pCurSGList;	   /* ptr to current SG list		     */

   /*------------------------------------------------*/
   /* First determine the type of I/O from the IORB. */
   /*------------------------------------------------*/

   switch (pIO->iorbh.CommandModifier) {
      case IOCM_READ:
	 type = SCB_READ;
	 break;

      case IOCM_READ_VERIFY:
	 type = SCB_READV;
	 break;

      case IOCM_WRITE:
	 type = SCB_WRITE;
	 break;

      case IOCM_WRITE_VERIFY:
	 type = SCB_WRITEV;
	 break;

      default:
	 pIO->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
	 return(COMPLETE);

   }

   rc = READY;		    /* assume the request will be setup completely */

   /*------------------------------------------------------------------*/
   /* Initialze the local variables so that they point to the SG       */
   /* list in the proper place.  This routine can be called in three   */
   /* cases:							       */
   /*								       */
   /*	1. Start of the request when nothing has been allocated.       */
   /*	2. Just before attempting to start the request when none or    */
   /*	   part of the request has been allocated.		       */
   /*	3. At interrupt time when the request is partially done and    */
   /*	   we need to finish setting it up.			       */
   /*								       */
   /* This section initializes the local variables so that they        */
   /* reflect the correct point in the I/O request regardless of the   */
   /* case above.  Also get a pointer to any SCBs that may be on the   */
   /* IORB if the request has already been started.  These SCBs are    */
   /* re-used.	Then set the status to indicate that the request is    */
   /* fully setup and the callout routine to the completition routine. */
   /* This is the default.  If it is not setup fully then the	       */
   /* appropriate status will be set before leaving.		       */
   /*------------------------------------------------------------------*/

   cCurSGs  = IORBWRKSP(pIO,cSGs);
   cTotSCBs = (pIO->cSGList / 16);		/* get integer #	    */
   cTotSCBs += (pIO->cSGList & 0xf) ? 1 : 0;	/* add 1 if remainder	    */
   cTotSCBs = (!cTotSCBs) ? 1 : cTotSCBs;	/* set to 1 if currently 0  */

   if (cCurSGs == 0) {
      CurRBA = pIO->RBA;
   }
   else {
      CurRBA = IORBWRKSP(pIO,NextRBA);
   }

   ppCurSGList = pIO->ppSGList + (16 * sizeof(SCATGATENTRY)) * cCurSGs;
   pCurSGList = pIO->pSGList + (cCurSGs * 16);
   cSGLeft = pIO->cSGList - (16 * cCurSGs);

   /*------------------------------------------------------------*/
   /* If the IORB has already been started then we have been	 */
   /* entered here at interrupt time.  Re-use the SCBs that are  */
   /* attached to insure that we will be able to restart the I/O */
   /* where it left off.					 */
   /*------------------------------------------------------------*/

   if (IORBWRKSP(pIO,Status) & IORBINTERRUPT) {
      npSCBListH = IORBWRKSP(pIO,npSCBH);
      IORBWRKSP(pIO,npSCBH) = NULL;
      IORBWRKSP(pIO,cSCBs) = 0;
   }
   else {
      npSCBListH = NULL;
   }

   IORBWRKSP(pIO,Status) &= ~IORBINCOMPLT;
   IORBWRKSP(pIO,Status) |= IORBCALLOUT;
   IORBWRKSP(pIO,npfnCallOut) =IOCC_ExecIODone;

   /*--------------------------------------------------------------*/
   /* We now point to the current point location in the I/O, which */
   /* is the beginning fo the I/O 99.9999 % of the time.  Start    */
   /* building SCBs until we are done or reach the chain limit.    */
   /*--------------------------------------------------------------*/

   for (i=cCurSGs; (i<cTotSCBs && IORBWRKSP(pIO,cSCBs)<MaxSCBs); i++) {

      /*------------------------------------------------------------*/
      /* Get an SCB either by re-using one if we were called an int */
      /* time or allocating one from the SCB pool.		    */
      /*------------------------------------------------------------*/

      if (npSCBListH != NULL) {
	 npSCBH = npSCBListH;
	 npSCBListH = npSCBListH->scbctrl.npNextSCBH;
      }
      else {
	 npSCBH = AllocSCB(npDCB,(PIORB)pIO);
      }

      if (npSCBH != NULL) {

	 /*------------------------------------------------------*/
	 /* If first loop iteration then intialize the SCB chain */
	 /* control pointers locally and in the IORB.		 */
	 /*------------------------------------------------------*/

	 if (i == cCurSGs) {
	    if (IORBWRKSP(pIO,npSCBH) == NULL) {
	       IORBWRKSP(pIO,npSCBH) = npSCBH;
	       npLastSCBH = NULL;
	    }
	    else {
	       npLastSCBH = IORBWRKSP(pIO,npSCBH);
	       while (npLastSCBH->scbctrl.npNextSCBH != NULL) {
		  npLastSCBH = npLastSCBH->scbctrl.npNextSCBH;
	       }
	    }
	 }

	 /*-------------------------------------------------------------*/
	 /* Now build the SCB based on the current location in the I/O. */
	 /* If there is only 1 SG entry then imbed the buffer address & */
	 /* count directly in the SCB and turn the SG bit (PT) in the	*/
	 /* SCB off (performance enhancement).	The SG list may be 0 in */
	 /* length for a verify (as done during format).  When done	*/
	 /* link the SCB to the current chain (if building a chain) and */
	 /* update the current location in the I/O.			*/
	 /*-------------------------------------------------------------*/

	 cCurSGList = min(16, cSGLeft);
	 if (cCurSGList != 0) {
	    cBlks = CountSGBlocks(pCurSGList, cCurSGList, pIO->BlockSize);
	    if (cCurSGList == 1) {
	       BuildSCB(&(npSCBH->scb), type, npDCB, CurRBA,
			pCurSGList->ppXferBuf, pCurSGList->XferBufLen,
			cBlks, pIO->BlockSize);
	       npSCBH->scb.Enable &= ~SCBEfPT;
	    }
	    else {
	       BuildSCB(&(npSCBH->scb), type, npDCB, CurRBA, ppCurSGList,
			cCurSGList*sizeof(SCATGATENTRY), cBlks, pIO->BlockSize);
	    }
	 }
	 else {
	    BuildSCB(&(npSCBH->scb), type, npDCB, CurRBA, 0L, 0L,
		     pIO->BlockCount, pIO->BlockSize);
	 }

	 if (pIO->Flags & XIO_DISABLE_HW_WRITE_CACHE ||
	     pIO->Flags & XIO_DISABLE_HW_READ_CACHE) {
	    npSCBH->scb.Enable |= SCBEfBB;
	 }

	 if (npLastSCBH != NULL) {
	    npLastSCBH->scbctrl.npNextSCBH = npSCBH;
	    npLastSCBH->scb.Enable |= SCBEfCC;
	    npLastSCBH->scb.ppNxtSCB = npSCBH->scbctrl.ppSCB;
	 }

	 npLastSCBH = npSCBH;
	 ppCurSGList += sizeof(SCATGATENTRY) * (ULONG)cCurSGList;
	 pCurSGList += cCurSGList;
	 CurRBA += (ULONG)cBlks;
	 cSGLeft -= cCurSGList;
	 IORBWRKSP(pIO,cSCBs)++;
	 IORBWRKSP(pIO,cSGs)++;
      }

      /*-------------------------------------------------------------*/
      /* Otherwise an SCB was not available.  Set the IORB status to */
      /* indicate that the request is not ready completely.  Set the */
      /* return code to INCOMPLETE if atleast 1 SCB was allocated    */
      /* and built, NORESOURCE if no SCBs were allocated.  Update    */
      /* the IORB with the current location of the I/O.  Leave the   */
      /* loop.							     */
      /*-------------------------------------------------------------*/

      else {

	 IORBWRKSP(pIO,Status) |= IORBINCOMPLT+IORBCALLOUT;
	 IORBWRKSP(pIO,npfnCallOut) = IOCC_ExecIOBldSCBChain;
	 INFMSG1("Unable to allocate SCB in Bld SCB chain for IORB %p \n\r",pIO);
	 DBSTOP();

	 rc = IORBWRKSP(pIO,npSCBH) ? INCOMPLETE : NORESOURCE;

	 IORBWRKSP(pIO,NextRBA) = CurRBA;
	 break;
      }

      IORBWRKSP(pIO,NextRBA) = CurRBA;
   }

   /*--------------------------------------------*/
   /* Free any SCBs that may be left.		 */
   /*--------------------------------------------*/

   if (npSCBListH) {
      FreeSCB(0, npSCBListH, npDCB);
   }

   return(rc);

}


/*************************************************************************/
/*									 */
/* IOCC_MM_ExecuteIO							 */
/*									 */
/*    This rotuine handles move mode execute I/O requests.		 */
/*									 */
/*	 Entry:  IOCC_MM_ExecuteIO(pIO, npDCB)				 */
/*									 */
/*		 pIO   - ptr to the IORB				 */
/*		 npDCB - ptr to the DCB 				 */
/*									 */
/*	 Exit: None.							 */
/*									 */
/*************************************************************************/

VOID IOCC_MM_ExecuteIO(PIORB_EXECUTEIO pIO, NPDCB npDCB)

{

   IOCC_MM_ExecIOBuildCtrlEl(pIO, npDCB);

}


/*************************************************************************/
/*									 */
/* IOCC_MM_ExecIOBuildCtrlEl						 */
/*									 */
/*    This rotuine handles building the control elements for move mode	 */
/*    I/O requests.							 */
/*									 */
/*	 Entry:  IOCC_MM_ExecIOBuildCtrlEl(pExecIO, npDCB)		 */
/*									 */
/*		 pIO   - ptr to the IORB				 */
/*		 npDCB - ptr to the DCB 				 */
/*									 */
/*	 Exit: INCOMPLETE if the element built is not the last element	 */
/*			  needed to complete the request		 */
/*	       COMPLETE   if the element built is the last one needed.	 */
/*									 */
/*************************************************************************/

USHORT IOCC_MM_ExecIOBuildCtrlEl(PIORB_EXECUTEIO pIO, NPDCB npDCB)

{

   CE_IOLIST	  ce_io;	 /* control element for doing the IO	    */
   ULONG	  CurRBA;	 /* current RBA for the current SCB	    */
   USHORT	  cBlks;	 /* count of blocks in the SG list	    */
   USHORT	  cBlksDone;	 /* count of blocks done already	    */
   USHORT	  i, j; 	 /* loop vars				    */
   USHORT	  cCurSG;	 /* count of SG entries in current element  */
   USHORT	  cSGLeft;	 /* count of SG entries left to do	    */
   USHORT	  cSGDone;
   PSCATGATENTRY  pSGList;	 /* ptr to current SG list		    */
   USHORT	  cCurSGList;	 /* count of SG entries for this element    */
   USHORT	  rc;		 /* the return code			    */

   rc = READY;			 /* assume this is last (only) element	    */

   /*----------------------------------------------------*/
   /* Determine where in the I/O we currently are.  cEl  */
   /* in the IORB workspace has the count of elements	 */
   /* completed so far.  If non-zero then each element	 */
   /* completed represents 16 SG entries done.	We	 */
   /* continue the I/O by building elements with upto 17 */
   /* entries until we are done.  Only the last element  */
   /* for the I/O is allowed to have 17 entries.	 */
   /*----------------------------------------------------*/

   cSGDone = (16 * IORBWRKSP2(pIO,cEl));
   cSGLeft = pIO->cSGList - cSGDone;
   pSGList = pIO->pSGList + cSGDone;

   if (cSGDone)
      cBlksDone = CountSGBlocks(pIO->pSGList, cSGDone, pIO->BlockSize);
   else
      cBlksDone = 0;

   CurRBA = pIO->RBA + cBlksDone;

   cCurSGList = (cSGLeft <= 17) ? cSGLeft : 16;
   cSGLeft   -= cCurSGList;

   /*-------------------------------------------------------*/
   /* Start building the control element by filling in the  */
   /* function and other stuff. 			    */
   /*-------------------------------------------------------*/

   INITCTRLEL(ce_io, 0, 0, npDCB, pIO);

   switch (pIO->iorbh.CommandModifier) {
      case IOCM_READ:
	 ce_io.ReqH.ci	     = FN_READLIST;
	 ce_io.ReqH.optctrl |= OP_RD;
	 break;

      case IOCM_READ_VERIFY:
	 ce_io.ReqH.ci	     = FN_READVERIFY;
	 ce_io.ReqH.optctrl |= OP_RD;
	 break;

      case IOCM_READ_PREFETCH:
	 pIO->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
	 FinishIORB((PIORB)pIO, npDCB);
	 return(COMPLETE);

      case IOCM_WRITE:
	 ce_io.ReqH.ci = FN_WRITELIST;
	 break;

      case IOCM_WRITE_VERIFY:
	 ce_io.ReqH.ci = FN_WRITEVERIFY;
	 break;

   }

   INITARSENSE(ce_io,pIO,npDCB);

   /*-------------------------------------------------------*/
   /* Now fill in the rest of the element that is specific  */
   /* to this part of the I/O.				    */
   /*-------------------------------------------------------*/

   for (j=0; j<cCurSGList; j++) {	    /* copy SG list */
      ce_io.SGList[j] = *(pSGList+j);
   }

   if (cCurSGList)
      cBlks = CountSGBlocks(pSGList, cCurSGList, pIO->BlockSize);
   else
      cBlks = pIO->BlockCount;

   ce_io.ReqH.length = sizeof(CE_IOLIST) -
		       ((17 - cCurSGList) * sizeof(SCATGATENTRY));
   ce_io.RBA = CurRBA;
   ce_io.cBlocks = cBlks;
   ce_io.BlockSize =
      (pIO->iorbh.CommandModifier == IOCM_READ_VERIFY) ? 0 : pIO->BlockSize;

   /*---------------------------------------------------------------*/
   /* The element is now ready.  Before sending it out set the IORB */
   /* status to indicate what to do next.  If there are SG entries  */
   /* left to do then set the incomplete bit and set the callout    */
   /* address to this routine.	If no SG entries left then clear    */
   /* the incomplete bit and set the callout address to the done    */
   /* routine.	Then start the element. 			    */
   /*---------------------------------------------------------------*/

   if (cSGLeft) {
      IORBWRKSP2(pIO,Status)	 |= IORBCALLOUT+IORBINCOMPLT;
      IORBWRKSP2(pIO,npfnCallOut) = IOCC_MM_ExecIOBuildCtrlEl;
      rc = INCOMPLETE;
   }
   else {
      IORBWRKSP2(pIO,Status)	 |= IORBCALLOUT;
      IORBWRKSP2(pIO,Status)	 &= ~IORBINCOMPLT;
      IORBWRKSP2(pIO,npfnCallOut) =IOCC_ExecIODone;
   }

   StartCtrlEl((PREQH)&ce_io, npDCB);

   return(rc);

}

/*************************************************************************/
/*									 */
/* IOCC_ExecIODone							 */
/*									 */
/*    This routine completes the IOCCExecuteIO request. 		 */
/*									 */
/*	 Entry:  IOCC_ExecIODone(pIO, npDCB)				 */
/*									 */
/*		 pIO - ptr to the IORB					 */
/*		 npDCB - ptr to the DCB 				 */
/*									 */
/*	 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_ExecIODone(PIORB_EXECUTEIO pIO, NPDCB npDCB)

{
   pIO->BlocksXferred = pIO->BlockCount;
   return(COMPLETE);
}



/*************************************************************************/
/*									 */
/* IOCC_DeviceControl							 */
/*									 */
/*    This routine handles the Device Control IORB for locate mode.	 */
/*									 */
/*	 Entry:  IOCC_DeviceControl(pDevCtrl, npDCB)			 */
/*									 */
/*		 pDevCtrl - ptr to the IORB.				 */
/*		 npDCB	  - ptr to the DCB.				 */
/*									 */
/*	 Exit: NONE							 */
/*									 */
/*************************************************************************/

VOID IOCC_DeviceControl(PIORB_DEVICE_CONTROL pDevCtrl, NPDCB npDCB)
{

   PIORB  pIORB;
   USHORT rc;

   switch (pDevCtrl->iorbh.CommandModifier) {

      /*---------------------------------------------------*/
      /* Abort.  This case handle aborting all active and  */
      /* waiting I/O for the specified device.		   */
      /*---------------------------------------------------*/

      case IOCM_ABORT:

	 SAVEIF();

	 /*----------------------------------------------------------------*/
	 /* For locate mode if the device is not IDLE then call the abort  */
	 /* routine to issue the abort. AbortDevice will then issue	   */
	 /* the abort immediate command and but it will NOT place the IORB */
	 /* on the active work queue.  The IORB will be ended.		   */
	 /*----------------------------------------------------------------*/

	 if (!(ACBTbl[npDCB->ai].status & MOVEMODE)) {
	    if (npDCB->state != IDLE) {
	       if (AbortDevice(npDCB, (PIORB)pDevCtrl, IOERR_CMD_ABORTED, 1)) {
		  pDevCtrl->iorbh.ErrorCode = IOERR_ADAPTER_DIAGFAIL;
		  FinishIORB((PIORB)pDevCtrl, npDCB);
	       }
	    }
	    else {
	       FinishIORB((PIORB)pDevCtrl, npDCB);
	    }
	 }

	 /*----------------------------------------------------------------*/
	 /* For move mode determine if the device is active (a non-NULL    */
	 /* active chain).  If so then call the abort routine which will   */
	 /* handle the abort.  If the abort routine is call then the IORB  */
	 /* will not be ended until the reply element is received.  If the */
	 /* abort routine is not called then the IORB is ended here.	   */
	 /*----------------------------------------------------------------*/

	 else {
	    if (npDCB->pActiveIORB) {
	       AbortDevice(npDCB, (PIORB)pDevCtrl, IOERR_CMD_ABORTED, 0);
	    }
	    else {
	       FinishIORB((PIORB)pDevCtrl, npDCB);
	    }
	 }

	 RESTOREIF();
	 break;


      /*--------------------------------------------------*/
      /* This case handles a reset.  The device is reset. */
      /*--------------------------------------------------*/

      case IOCM_RESET:

	 INFMSG("Received RESET command\n\r");

	 /*-----------------------------------------------------*/
	 /* Call the reset routine to reset the device. 	*/
	 /*-----------------------------------------------------*/

	 ResetDevice(npDCB, (PIORB)pDevCtrl);

	 break;


      /*--------------------------------------*/
      /* Suspend and Resume are not supported */
      /*--------------------------------------*/

      case IOCM_SUSPEND:
      case IOCM_RESUME:
	 pDevCtrl->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
	 FinishIORB((PIORB)pDevCtrl, npDCB);
	 break;


      /*--------------------------*/
      /* Lock/Unlock/Eject media. */
      /*--------------------------*/

      case IOCM_LOCK_MEDIA:
      case IOCM_UNLOCK_MEDIA:
      case IOCM_EJECT_MEDIA:

	 /*------------------------------------------------*/
	 /* If removable media device it is OK to continue */
	 /*------------------------------------------------*/

	 if (npDCB->UnitInfo.UnitFlags & UF_REMOVABLE) {

	    /*-------------------------------------------------*/
	    /* If locate mode then call the setup routine.  If */
	    /* setup OK then start it if the device is IDLE.   */
	    /* Otherwise queue the command.		       */
	    /*-------------------------------------------------*/

	    if (!(ACBTbl[npDCB->ai].status & MOVEMODE)) {

	       SAVEIF();

	       rc = IOCM_MediaControl((PIORB)pDevCtrl, npDCB);

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

	       RESTOREIF();
	    }

	    /*-------------------------------------------------*/
	    /* Otherwise call the move mode routine.	       */
	    /*-------------------------------------------------*/

	    else {
	       IOCM_MM_MediaControl((PIORB)pDevCtrl, npDCB);
	    }
	 }
	 else {
	    pDevCtrl->iorbh.ErrorCode = IOERR_CMD_NOT_SUPPORTED;
	    FinishIORB((PIORB)pDevCtrl, npDCB);
	 }

	 break;

   }

}

/*************************************************************************/
/*									 */
/* IOCM_MediaControl							 */
/*									 */
/*    This routine builds a passthru SCB containing a SCSI		 */
/*    Prevent/Allow Medium Removal.  It is for locate mode devices.	 */
/*									 */
/*	 Entry:  IOCM_MediaControl(pIORB, npDCB)			 */
/*									 */
/*		 pIORB	  - ptr to the IORB.				 */
/*		 npDCB	  - ptr to the DCB.				 */
/*									 */
/*	 Exit:								 */
/*									 */
/*************************************************************************/

USHORT IOCM_MediaControl(PIORB pIORB, NPDCB npDCB )
{

   NPSCBH npSCBH;

   IORBWRKSP(pIORB,Status)  &= ~IORBINCOMPLT;

   if ((npSCBH = AllocSCB(npDCB, pIORB)) != NULL) {

      BuildSCB(&(npSCBH->scb), SCB_SENDOTHER, npDCB,
	       (ULONG)sizeof(SCSICDB6),
	       0L,
	       0L, 0, 0);

      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;

      if (pIORB->CommandModifier == IOCM_LOCK_MEDIA) {
	 npSCBH->scb.EXT.CDB.SCSIcdb[0] = SCSI_LOCK_UNLOCK;
	 npDCB->status |= UNITLOCKED;
	 npSCBH->scb.EXT.CDB.SCSIcdb[4] = 1;
      }
      else if (pIORB->CommandModifier == IOCM_UNLOCK_MEDIA) {
	 npSCBH->scb.EXT.CDB.SCSIcdb[0] = SCSI_LOCK_UNLOCK;
	 npDCB->status &= ~UNITLOCKED;
	 npSCBH->scb.EXT.CDB.SCSIcdb[4] = 0;
      }
      else {					     /* eject media  */
	 npSCBH->scb.EXT.CDB.SCSIcdb[0] = SCSI_START_STOP_UNIT;
	 npSCBH->scb.EXT.CDB.SCSIcdb[4] = 2;
      }

      npSCBH->scb.Enable &= ~SCBEfPT;

      IORBWRKSP(pIORB,Status) &= ~IORBCALLOUT;
      IORBWRKSP(pIORB,Status) |= IORBSCBLONG;
      IORBWRKSP(pIORB,npSCBH)  = npSCBH;
      IORBWRKSP(pIORB,cSCBs)   = 1;

      return(READY);
   }

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

/*************************************************************************/
/*									 */
/* IOCM_MM_MediaControl 						 */
/*									 */
/*    This routine builds a control element with a CDB to do the lock/	 */
/*    unlock function.							 */
/*									 */
/*	 Entry:  IOCM_MM_MediaControl(pIORB, npDCB)			 */
/*									 */
/*		 pIORB	  - ptr to the IORB.				 */
/*		 npDCB	  - ptr to the DCB.				 */
/*									 */
/*	 Exit:								 */
/*									 */
/*************************************************************************/

VOID IOCM_MM_MediaControl(PIORB pIORB, NPDCB npDCB )
{

   CE_EXEC_CDB ce_execcdb;	  /* buffer for largest possible ce  */

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

   INITCTRLEL(ce_execcdb,(sizeof(CE_EXEC_CDB)-(17*sizeof(SCATGATENTRY))),
	      FN_SENDSCSI,npDCB,pIORB);
   INITARSENSE(ce_execcdb,pIORB,npDCB);
   IORBWRKSP2(pIORB,Status) &= ~IORBCALLOUT;
   IORBWRKSP2(pIORB,Status) &= ~IORBINCOMPLT;

   ce_execcdb.cBytes	    = 0;
   ce_execcdb.ReqH.optctrl |= 6 << 8;

   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;

   if (pIORB->CommandModifier == IOCM_LOCK_MEDIA) {
      ce_execcdb.cdb[0] = SCSI_LOCK_UNLOCK;
      npDCB->status |= UNITLOCKED;
      ce_execcdb.cdb[4] = 1;
   }
   else if (pIORB->CommandModifier == IOCM_UNLOCK_MEDIA) {
      ce_execcdb.cdb[0] = SCSI_LOCK_UNLOCK;
      npDCB->status &= ~UNITLOCKED;
      ce_execcdb.cdb[4] = 0;
   }
   else {					  /* eject media */
      ce_execcdb.cdb[0] = SCSI_START_STOP_UNIT;
      ce_execcdb.cdb[4] = 2;
   }

   StartCtrlEl((PREQH)&ce_execcdb, npDCB);

}

