/*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 = CDQUEUE.C
 *
 * DESCRIPTIVE NAME = Request queuing routines for OS/2 CD-ROM Manager
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   Maintains queues of IORB requests that are awaiting
 *             processing.  Also provides routing of IORBs to
 *             Adapter Device Drivers.
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "cdh.h"

VOID   CallADD (NPUNITCB, NPIORB);
USHORT ProcessError (NPUNITCB, NPIORB);
UCHAR  NEAR MapIORBError(USHORT);
USHORT NEAR MapSenseData(NPIORB);
VOID   NEAR TraceError(NPIORB_CDB);
VOID   NEAR PurgeWaitingQueue (NPUNITCB);
USHORT NEAR RemoveWaitingQueue (NPUNITCB, NPIORB);


/****************************************************************************
 *
 * FUNCTION NAME = QueueIORB
 *
 * DESCRIPTION   = Put a request on a waiting queue.
 *
 *          Put a request on the waiting queue in the unit control block
 *          for the specified unit.
 *
 *          VOID PutWaitingQueue  (NPUNITCB pUnitCB, NPIORB pIORB)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 pIORB            - Pointer to IORB
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID QueueIORB (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   pIORB;

{
   NPIORB_DMWORK pDMWork;
   NPIORB        pPrevIORB;

   PUSHFLAGS;
   DISABLE;

   pDMWork = (NPIORB_DMWORK) &(pIORB->DMWorkSpace);
   pDMWork->WaitingQueueLink = 0;
   pDMWork->pUnitCB = pUnitCB;

   if (pUnitCB->WaitingQueue.Head == 0)
   {
      pUnitCB->WaitingQueue.Head = pIORB;
      pUnitCB->WaitingQueue.Tail = pIORB;
   }
   else
   {
      pPrevIORB = pUnitCB->WaitingQueue.Tail;
      pDMWork = (NPIORB_DMWORK) &(pPrevIORB->DMWorkSpace);
      pDMWork->WaitingQueueLink = pIORB;
      pUnitCB->WaitingQueue.Tail = pIORB;
   }

   pUnitCB->NumReqsWaiting++;
   CD_NumReqsWaiting++;

   POPFLAGS;

}


/****************************************************************************
 *
 * FUNCTION NAME = PullWaitingQueue
 *
 * DESCRIPTION   = Pull a request from a waiting queue.
 *
 *                 USHORT PullWaitingQueue  (NPUNITCB pUnitCB, NPIORB *pIORB)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 pIORB            - returned pointer to IORB
 *
 * OUTPUT        = USHORT           - = 0, Request pulled
 *                                    <> 0, No request pulled, queues empty
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT PullWaitingQueue (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   FAR *pIORB;

{
   USHORT rc;
   NPIORB pReq;
   NPIORB_DMWORK pDMWork;

   PUSHFLAGS;
   DISABLE;

   /*
   ** Make sure there's a request waiting to be processed before
   ** searching the queues.
   */
   if (pUnitCB->NumReqsWaiting == 0)
   {
      rc = ERROR;
      goto PullRet;
   }

   pReq = pUnitCB->WaitingQueue.Head;
   pDMWork = (NPIORB_DMWORK) &(pReq->DMWorkSpace);

   pUnitCB->WaitingQueue.Head = pDMWork->WaitingQueueLink;
   pDMWork->WaitingQueueLink = 0;

   if (pUnitCB->WaitingQueue.Head == 0)
         pUnitCB->WaitingQueue.Tail = 0;

   *pIORB = pReq;
   pUnitCB->NumReqsWaiting--;
   CD_NumReqsWaiting--;
   rc = NO_ERROR;


PullRet:
   POPFLAGS;
   return(rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = PurgeWaitingQueue
 *
 * DESCRIPTION   = Purge the waiting queue on a media changed errror.
 *
 *                 USHORT PurgeWaitingQueue  (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PurgeWaitingQueue (pUnitCB)

NPUNITCB pUnitCB;

{
   NPIORB  pIORB;
   USHORT  AwakeCount;

   while (PullWaitingQueue (pUnitCB, (NPIORB FAR *)&pIORB) == NO_ERROR)
   {
      pIORB->Status = IORB_DONE + IORB_ERROR;
      pIORB->ErrorCode = IOERR_MEDIA_CHANGED;
      DevHelp_ProcRun((ULONG)pIORB, &AwakeCount);
   }
}


/****************************************************************************
 *
 * FUNCTION NAME = RemoveWaitingQueue
 *
 * DESCRIPTION   = Remove an entry from the waiting queue.
 *
 *                 USHORT RemoveWaitingQueue  (NPUNITCB pUnitCB, NPIORB pIORB)
 *
 * INPUT         = pUnitCB          - Pointer to UnitCB
 *                 pIORB            - Pointer to IORB
 *
 * OUTPUT        = USHORT           - YES, iorb removed
 *                                    NO,  iorb not removed
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT RemoveWaitingQueue (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   pIORB;

{
   NPIORB pCurReq, pPrevReq;
   NPIORB_DMWORK  pDMWorkCur, pDMWorkPrev;
   USHORT removed = NO;

   PUSHFLAGS;
   DISABLE;

   /*
   ** Make sure the waiting queue isnt empty
   */
   if (pUnitCB->NumReqsWaiting == 0)
      goto RemoveRet;

   /*
   ** If it's at the head simply remove it from the head
   */
   if (pUnitCB->WaitingQueue.Head == pIORB)
   {
      pDMWorkCur = (NPIORB_DMWORK) &(pIORB->DMWorkSpace);
      pUnitCB->WaitingQueue.Head = pDMWorkCur->WaitingQueueLink;
      if (pDMWorkCur->WaitingQueueLink == 0)
         pUnitCB->WaitingQueue.Tail = 0;
      else
         pDMWorkCur->WaitingQueueLink = 0;

      removed = YES;
      pUnitCB->NumReqsWaiting--;
      CD_NumReqsWaiting--;
      goto RemoveRet;
   }
   /*
   ** Not at the head, must search for it
   */
   pCurReq = pUnitCB->WaitingQueue.Head;
   pDMWorkCur =  (NPIORB_DMWORK) &(pCurReq->DMWorkSpace);

   while (pCurReq != 0)
   {
      if (pCurReq == pIORB)
      {
         pDMWorkPrev->WaitingQueueLink = pDMWorkCur->WaitingQueueLink;
         if (pUnitCB->WaitingQueue.Tail == pCurReq)
            pUnitCB->WaitingQueue.Tail = pDMWorkCur->WaitingQueueLink;

         removed = YES;
         break;
      }
      pPrevReq = pCurReq;
      pDMWorkPrev = (NPIORB_DMWORK) &(pPrevReq->DMWorkSpace);

      if (pDMWorkCur->WaitingQueueLink != 0)
      {
         pCurReq = pDMWorkCur->WaitingQueueLink;
         pDMWorkCur =  (NPIORB_DMWORK) &(pCurReq->DMWorkSpace);
      }
   }

RemoveRet:
   POPFLAGS;
   return(removed);
}


/****************************************************************************
 *
 * FUNCTION NAME = SubmitIORB_Wait
 *
 * DESCRIPTION   = Submit an IORB, wait till done
 *
 *       This function will submit a request and wait till it's done.
 *       If the device is idle, the request will be submitted, else the
 *       request till be queued on the unit's waiting queue.
 *
 *       USHORT SubmitIORB_Wait (pUnitCB, pIORB)
 *
 * INPUT         = pUnitCB          - pointer to unit control block
 *                 pIORB            - pointer to IORB
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT NEAR SubmitIORB_Wait (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   pIORB;

{
   USHORT rc;
   NPIORB pIORBq;
   NPIORB_DMWORK pDMWork;

   PUSHFLAGS;
   DISABLE;

   pDMWork = (NPIORB_DMWORK) &(pIORB->DMWorkSpace);
   pDMWork->pUnitCB = pUnitCB;

   /*
   ** If the device is idle, then submit the request
   */
   if (pUnitCB->NumReqsInProgress == 0)
   {
      if (pUnitCB->NumReqsWaiting == 0)
      {
         pIORBq = pIORB;
      }
      else
      {
         PullWaitingQueue (pUnitCB, (NPIORB FAR *)&pIORBq);
         QueueIORB (pUnitCB, pIORB);
      }
      pUnitCB->NumReqsInProgress++;
      CD_NumReqsInProgress++;

      ENABLE;
      CallADD(pUnitCB, pIORBq);
   }

   /*
   ** else queue the request
   */
   else
      QueueIORB (pUnitCB, pIORB);

   /*
   ** Block until the request is done
   */
   DISABLE;
   while ( !( pIORB->Status & IORB_DONE ) )
   {
      DevHelp_ProcBlock ((ULONG)pIORB, -1L, 1);
      DISABLE;                          /* Block does an enable             */
   }
   ENABLE;

   POPFLAGS;

   rc = ProcessError(pUnitCB, pIORB);

   return (rc);
}


/****************************************************************************
 *
 * FUNCTION NAME = f_SubmitRequestsToADD
 *
 * DESCRIPTION   = Submit Requests to an Adapter Device Driver
 *
 * INPUT         = pUnitCB          - pointer to unit control block
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID FAR f_SubmitRequestsToADD (pUnitCB)

NPUNITCB pUnitCB;
{
   SubmitRequestsToADD (pUnitCB);
}


/****************************************************************************
 *
 * FUNCTION NAME = SubmitRequestsToADD
 *
 * DESCRIPTION   = Submit Requests to an Adapter Device Driver
 *
 *     This function submits requests to the Adapter Device Driver which
 *     manages this unit.  Requests are pulled from the unit's waiting queue
 *     and submitted to the ADD if the ADD's recommended queuing count
 *     has not been exceeded.
 *
 *     USHORT SubmitRequestsToADD (pUnitCB)
 *
 * INPUT         = pUnitCB          - pointer to unit control block
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID NEAR SubmitRequestsToADD (pUnitCB)

NPUNITCB pUnitCB;

{
   NPIORB pIORB;
   USHORT i;
   USHORT NumIORBsBuilt = 0;

   PUSHFLAGS;
   DISABLE;

   /*
   ** If the number of requests already submitted to the ADD is
   ** less than the ADD's recommended queuing count, then submit
   ** additional requests.
   */
   for (i=pUnitCB->NumReqsInProgress; i < pUnitCB->UnitInfo.QueuingCount; i++)
   {

      if (PullWaitingQueue (pUnitCB, (NPIORB FAR *)&pIORB) != NO_ERROR)
         break;

      /*
      ** We've pulled another request from the queue, so build the IORB
      ** for it and chain it to the previous if we've built more than one.
      */
      pUnitCB->NumReqsInProgress++;
      CD_NumReqsInProgress++;
      NumIORBsBuilt++;

      ENABLE;
      CallADD(pUnitCB, pIORB);
      DISABLE;
   }

   ENABLE;
   POPFLAGS;

}


/****************************************************************************
 *
 * FUNCTION NAME = CallADD
 *
 * DESCRIPTION   = Call the Adapter Driver Entry Point
 *
 *         This function calls the adapter driver entry point with the
 *         specified IORB chain.
 *
 *         VOID CallADD (pUnitCB, pIORB)
 *
 * INPUT         = pUnitCB          - pointer to unit control block
 *                 pIORB            - pointer to IORB chain
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID CallADD (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   pIORB;

{
/*
** if (TraceFlags != 0)
**    Trace(TRACE_IORB | TRACE_ENTRY, (PBYTE)pIORB, pUnitCB);
*/
   /*
   ** If we're issuing a request to a PANSONIC 501 and it's one
   ** one of the vendor unique audio commands, then just turn on
   ** the high bit of the command byte so the command opcode is right.
   */
   if (pUnitCB->DeviceInfo.product_id_code == PANASONIC_501)
   {
      if (  ( ((NPIORB_CDB)pIORB)->CDB.byte_0 >= SCSI_READ_SUB_CHAN) &&
            ( ((NPIORB_CDB)pIORB)->CDB.byte_0 <= SCSI_PAUSE_RESUME) )
      {
         ((NPIORB_CDB)pIORB)->CDB.byte_0 |= 0x80;
      }
   }

   /*
   **  If ATAPI command, change CDB command length to 12.
   */

   if ( (pUnitCB->DeviceInfo.interface_type == INTERFACE_ATAPI) &&
        (pIORB->CommandCode == IOCC_ADAPTER_PASSTHRU) )
   {
      ((NPIORB_CDB)pIORB)->apt.ControllerCmdLen = ATAPI_CDB_LENGTH;
   }

   /*
   **  Call the adapter device driver.
   */

   (pUnitCB->AdapterDriverEP) ((PVOID) pIORB);
}




/****************************************************************************
 *
 * FUNCTION NAME = NotifyDoneIORB
 *
 * DESCRIPTION   = Notify routine when IORB is done in ADD
 *
 *   This function is the notification routine called by an Adapter Device
 *   Driver when an IORB is done.  Error status and blocks transferred
 *   are updated in the associated Request Packet or Request List entry.  If
 *   the request is a Strategy-2 Request List Entry, the file system is
 *   notified that the request has completed.  If the request is a
 *   Strategy-1 Request Packet, the thread waiting on the request is woken up.
 *
 *   VOID NotifyDoneIORB (PIORB fpIORB)
 *
 * INPUT         = fpIORB            - far pointer to completed IORB
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID _loadds FAR NotifyDoneIORB (fpIORB)

PIORB fpIORB;

{
   NPUNITCB pUnitCB;
   NPIORB   pIORB;
   USHORT   AwakeCount;
   NPIORB_DMWORK pDMWork;

   pIORB = (NPIORB) (OFFSETOF(fpIORB));
   pDMWork = (NPIORB_DMWORK) &(pIORB->DMWorkSpace);
   pUnitCB = (NPUNITCB) pDMWork->pUnitCB;


   if (  (pIORB->Status & IORB_ERROR) &&
        !(pIORB->Status & IORB_RECOV_ERROR) )
   {
      /*
      ** If media changed error, then purge the waiting queue
      */
      if (pIORB->ErrorCode == IOERR_MEDIA_CHANGED)
         PurgeWaitingQueue (pUnitCB);

      else if (pDMWork->pCoReqIORB != 0)
      {
         if (RemoveWaitingQueue (pUnitCB, pDMWork->pCoReqIORB) == YES)
         {
            pDMWork->pCoReqIORB->Status = IORB_DONE | IORB_ERROR;
            pDMWork->pCoReqIORB->ErrorCode = pIORB->ErrorCode;
            DevHelp_ProcRun((ULONG)pDMWork->pCoReqIORB, &AwakeCount);
         }
      }
   }

   if (pDMWork->pCoReqIORB == 0)
      DevHelp_ProcRun((ULONG)pIORB, &AwakeCount);


   PUSHFLAGS;
   DISABLE;
   pUnitCB->NumReqsInProgress --;
   CD_NumReqsInProgress--;
   POPFLAGS;

   SubmitRequestsToADD(pUnitCB);

}


/****************************************************************************
 *
 * FUNCTION NAME = ProcessError
 *
 * DESCRIPTION   = Post process a completed IORB's error code
 *
 *                 USHORT ProcessError (pUnitCB, pIORB)
 *
 * INPUT         = pUnitCB          - pointer to unit control block
 *                 pIORB            - pointer to IORB
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT ProcessError (pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB   pIORB;
{
   USHORT RP_Status;

   RP_Status = STDON;

   if (  (pIORB->Status & IORB_ERROR) &&
        !(pIORB->Status & IORB_RECOV_ERROR) )
   {
      RP_Status = MapSenseData(pIORB);

      if (RP_Status == 0)
         (UCHAR) RP_Status = MapIORBError(pIORB->ErrorCode);

      RP_Status |= STDON + STERR;

      if (RP_Status == STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA)
      {
         pUnitCB->Flags |= UCF_UNCERTAIN_MEDIA;
         pUnitCB->DeviceInfo.Audio.status.paused = FALSE;
         pUnitCB->DeviceInfo.Audio.status.last_start_location.min   = 0;
         pUnitCB->DeviceInfo.Audio.status.last_start_location.sec   = 0;
         pUnitCB->DeviceInfo.Audio.status.last_start_location.frame = 0;
         pUnitCB->DeviceInfo.Audio.status.last_end_location.min     = 0;
         pUnitCB->DeviceInfo.Audio.status.last_end_location.sec     = 0;
         pUnitCB->DeviceInfo.Audio.status.last_end_location.frame   = 0;

/*       PurgeWaitingQueue(pUnitCB, ERROR_I24_UNCERTAIN_MEDIA);             */
      }

      /*
      ** If a reset condition occurred, then reset lock and volume settings
      */
      if ( (((NPIORB_CDB)pIORB)->sense_data.sense_key == SCSI_SK_UNITATTN) &&
           (((NPIORB_CDB)pIORB)->sense_data.additional_sense_code == ASC_RESET))
      {
         pUnitCB->DeviceInfo.Audio.capabilities &= ~DCAPS_DOOR_LOCKED;
         pUnitCB->DeviceInfo.Audio.channel.input_0  = 0;
         pUnitCB->DeviceInfo.Audio.channel.volume_0 = 0xFF;
         pUnitCB->DeviceInfo.Audio.channel.input_1  = 1;
         pUnitCB->DeviceInfo.Audio.channel.volume_1 = 0xFF;
      }

      TraceError( (NPIORB_CDB) pIORB);
   }
   return(RP_Status);
}


/****************************************************************************
 *
 * FUNCTION NAME = MapSenseData
 *
 * DESCRIPTION   = Maps a SCSI sense data to an ERROR_I24 error code.
 *
 *                 USHORT  MapSenseData (NPIORB IORBErrorCode)
 *
 * INPUT         = IORBError        - IORB error code
 *
 * OUTPUT        = UCHAR            - ERROR_I24 error code
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

typedef struct _ASC_TABLE_ENTRY
{
   UCHAR    additional_sense_code;
   USHORT   I24_ErrorCode;
} ASC_TABLE_ENTRY;

typedef struct _SK_TABLE_ENTRY
{
   UCHAR    asc_count;
   USHORT   I24_ErrorCode;
} SK_TABLE_ENTRY;


USHORT MapSenseData (pIORB)

NPIORB pIORB;
{

   static ASC_TABLE_ENTRY ASCT_NotRdy[] =
   {
      {ASC_INCOMPATIBLE_CARTRIDGE,  STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
      {0xFF,                        STDON + STERR + ERROR_I24_NOT_READY}
   };

   static ASC_TABLE_ENTRY ASCT_MediumError[] =
   {
     {ASC_UNRECOVERED_ERROR,      STDON + STERR + ERROR_I24_CRC},
     {ASC_NO_ADDRESS_MARK,        STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
     {ASC_SEEK_POSITIONING_ERROR, STDON + STERR + ERROR_I24_SEEK},
     {ASC_DATA_SYNC_ERROR,        STDON + STERR + ERROR_I24_READ_FAULT},
     {ASC_INCOMPATIBLE_CARTRIDGE, STDON + STERR + ERROR_I24_NOT_DOS_DISK},
     {0xFF,                       STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
   };

   static ASC_TABLE_ENTRY ASCT_HardwareError[] =
   {
      {ASC_SEEK_POSITIONING_ERROR, STDON + STERR + ERROR_I24_SEEK},
   };

   static ASC_TABLE_ENTRY ASCT_IllegalRequest[] =
   {
      {ASC_ILLEGAL_MODE_FOR_TRACK, STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
      {ASC_ILLEGAL_LBA,            STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
      {ASCV_NOT_AUDIO_TRACK,       STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
      {ASCV_NOT_DATA_TRACK,        STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
      {ASCV_NOT_AUDIO_PLAY_STATE,  STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND},
      {0xFF,                       STDON + STERR + ERROR_I24_INVALID_PARAMETER},
   };

   static ASC_TABLE_ENTRY ASCT_UnitAttention[] =
   {
      {ASC_MEDIUM_CHANGED,        STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA},
      {ASC_RESET,                 STDON + STERR + ERROR_I24_GEN_FAILURE},
      {0xFF,                      STDON + STERR + ERROR_I24_UNCERTAIN_MEDIA},
   };

   static ASC_TABLE_ENTRY ASCT_BlankCheck[] =
   {
      {ASC_ILLEGAL_MODE_FOR_TRACK,  STDON + STERR + ERROR_I24_BAD_COMMAND},
   };


   static SK_TABLE_ENTRY SenseKeyTable[] =
   {
     { 0, STDON },                                    /* 0 - NOSENSE     */
     { 0, STDON },                                    /* 1 - RECERR      */
//   { 0, STDON + STERR + ERROR_I24_NOT_READY },      /* 2 - NOTRDY      */

     { sizeof(ASCT_NotRdy)/sizeof(ASC_TABLE_ENTRY),
             (USHORT) ASCT_NotRdy },                  /* 2 - NOTRDY      */

     { sizeof(ASCT_MediumError)/sizeof(ASC_TABLE_ENTRY),
             (USHORT) ASCT_MediumError },             /* 3 - MEDIUMERR   */

     { sizeof(ASCT_HardwareError)/sizeof(ASC_TABLE_ENTRY),
             (USHORT) ASCT_HardwareError },           /* 4 - HARDWAREERR */

     { sizeof(ASCT_IllegalRequest)/sizeof(ASC_TABLE_ENTRY),
             (USHORT) ASCT_IllegalRequest },          /* 5 - ILLEGALREQ  */

     { sizeof(ASCT_UnitAttention)/sizeof(ASC_TABLE_ENTRY),
             (USHORT) ASCT_UnitAttention },           /* 6 - UNITATTN    */


     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* 7 - DATAPROTECT */


     { sizeof(ASCT_BlankCheck)/sizeof(ASC_TABLE_ENTRY),
             (USHORT) ASCT_BlankCheck },              /* 8 - BLANKCHK    */

     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* 9 - Vendor Spec */
     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* A - COPYABORT   */
     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* B - ABORTEDCMD  */
     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* C - EQUAL       */
     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* D - VOLOVERFLOW */
     { 0, STDON + STERR + ERROR_I24_GEN_FAILURE},     /* E - MISCOMPARE  */
   };



   UCHAR rc, sense_key, additional_sense_code, asc_count;
   USHORT i;
   ASC_TABLE_ENTRY NEAR *pTable;

   rc = 0;

   /*
   ** Make sure sense data is available
   */
   if ( (pIORB->Status & IORB_STATUSBLOCK_AVAIL) &&
        ( ((NPSCSI_STATUS_BLOCK)pIORB->pStatusBlock)->Flags &
                                      STATUS_SENSEDATA_VALID)  &&
        ( ((NPIORB_CDB) pIORB)->sense_data.error_code == 0x70 ||
          ((NPIORB_CDB) pIORB)->sense_data.error_code == 0x71) )
   {
      sense_key = ((NPIORB_CDB)pIORB)->sense_data.sense_key;
      additional_sense_code =
                 ((NPIORB_CDB)pIORB)->sense_data.additional_sense_code;

      if (sense_key > 0x0E)
         return (STDON + STERR + ERROR_I24_GEN_FAILURE);

      asc_count = SenseKeyTable[sense_key].asc_count;

      if (asc_count == 0)
         return (SenseKeyTable[sense_key].I24_ErrorCode);

      (USHORT) pTable = SenseKeyTable[sense_key].I24_ErrorCode;

      for (i = 0; i < asc_count; i++)
         if (pTable[i].additional_sense_code == additional_sense_code)
         {
            return (pTable[i].I24_ErrorCode);
         }

      if (pTable[i-1].additional_sense_code == 0xFF)
         return (pTable[i-1].I24_ErrorCode);

      return (STDON + STERR + ERROR_I24_GEN_FAILURE);
   }
   return(0);
}


/****************************************************************************
 *
 * FUNCTION NAME = MapIORBError
 *
 * DESCRIPTION   = Maps an IORB error code to an ERROR_I24 error code.
 *
 *                 UCHAR  MapIORBError (USHORT IORBErrorCode)
 *
 * INPUT         = IORBError        - IORB error code
 *
 * OUTPUT        = UCHAR            - ERROR_I24 error code
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

typedef struct _ERROR_TABLE_ENTRY
{
   USHORT   IORB_ErrorCode;
   UCHAR    I24_ErrorCode;
} ERROR_TABLE_ENTRY;


UCHAR MapIORBError(IORB_ErrorCode)

USHORT IORB_ErrorCode;

{
   static ERROR_TABLE_ENTRY ErrorTable[] =
   {
      {IOERR_UNIT_NOT_READY,        ERROR_I24_NOT_READY},
      {IOERR_RBA_ADDRESSING_ERROR,  ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_RBA_LIMIT,             ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_RBA_CRC_ERROR,         ERROR_I24_CRC},
      {IOERR_MEDIA_NOT_FORMATTED,   ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_MEDIA_NOT_SUPPORTED,   ERROR_I24_SECTOR_NOT_FOUND},
      {IOERR_MEDIA_WRITE_PROTECT,   ERROR_I24_WRITE_PROTECT},
      {IOERR_MEDIA_CHANGED,         ERROR_I24_UNCERTAIN_MEDIA},
      {IOERR_MEDIA_NOT_PRESENT,     ERROR_I24_NOT_READY},
      {IOERR_CMD_NOT_SUPPORTED,     ERROR_I24_BAD_COMMAND},
      {IOERR_CMD_SYNTAX,            ERROR_I24_BAD_COMMAND},
      {-1,-1},
   };

   USHORT i;

   /*
   ** Map the IORB error to the corresponding ERROR_I24 error code
   */
   for (i = 0; ErrorTable[i].IORB_ErrorCode != -1; i++)
     if (ErrorTable[i].IORB_ErrorCode == IORB_ErrorCode)
        return(ErrorTable[i].I24_ErrorCode);

   return(ERROR_I24_GEN_FAILURE);
}


/****************************************************************************
 *
 * FUNCTION NAME = TraceError
 *
 * DESCRIPTION   = Logs an error in the trace buffer
 *
 *                 VOID  TraceError (NPIORB pIORB)
 *
 * INPUT         = pIORB            - pointer to IORB
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID TraceError (pIORB)

NPIORB_CDB pIORB;

{
   USHORT i;
   UCHAR NEAR *pCDB;
   UCHAR NEAR *pSenseData;

   pCDB = (UCHAR NEAR *) &pIORB->CDB;
   pSenseData = (UCHAR NEAR *) &pIORB->sense_data;

   /*
   ** Only trace adapter passthru commands
   */
   if (pIORB->apt.iorbh.CommandCode == IOCC_ADAPTER_PASSTHRU)
   {
      (USHORT) *pCDTraceHead     = pIORB->apt.iorbh.UnitHandle;
      (USHORT) *(pCDTraceHead+2) = pIORB->apt.iorbh.ErrorCode;

      for (i = 0; i < 12; i++)
         *(pCDTraceHead+4+i) = *(pCDB+i);

      for (i = 0; i < 16; i++)
         *(pCDTraceHead+16+i) = *(pSenseData+i);

      pCDTraceHead += 32;

      if (pCDTraceHead >= pCDTraceEnd)
         pCDTraceHead = pCDTraceBuf;
   }
}

