/*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.                                */
/*                                                                           */
/*****************************************************************************/

 #define INCL_DOSINFOSEG
 #define INCL_NO_SCB
 #define INCL_INITRP_ONLY
 #include "os2.h"                  // \DRV6\H
 #include "dos.h"                  // \DRV6\H
 #include "sas.h"                  // \DRV6\H
 #include "devcmd.h"               // \DRV6\H

 #include "iorb.h"                 // \DRV6\SRC\DEV\DASD\DISKH
 #include "reqpkt.h"               // \DRV6\SRC\DEV\DASD\DISKH
 #include "addcalls.h"             // \DRV6\SRC\DEV\DASD\DISKH
 #include "dskinit.h"
 #include "devclass.h"

 #include <scsi.h>
 #include <cdbscsi.h>
 #include "cmd.h"
 #include "devhelp.h"              // \DRV6\SRC\DEV\DASD\DISKH
 #include "cdb.h"
 #include "proto.h"
 #include "rmbase.h"
 #include "rmcalls.h"
 #include <string.h>
 #include <memory.h>


#define BAD_PARMS        0x8113
#define STATUS_QUIET_FAIL   0x8015 | STATUS_DONE
static BOOL Allocated=FALSE;
BYTE Adapter[16]="MITSUMI$";
USHORT DeviceHandle=0;
extern USHORT near AdapterBase, near StatusRegister,near DataStatusRegister, near IrqNum, near IrqMode;
extern PUCHAR near YieldFlag;
extern PGINFOSEG near PGinfo;
extern void ScaleClock(UCHAR CpuType);
extern UCHAR near traceflag;
extern BOOL near ReadStatusLate;
extern UCHAR near wDrvVer[4];
extern BOOL near Lu002;
static char devstring[]="CDROM_0 ";

static UCHAR ProductIDMsg[] =                                    \
"\n\r"                                                           \
"IBM OS/2 MITFX001.ADD 1.13 (950120)\n\r"                        \
"?ITSUMI CD-ROM ?       ?    Port: 0?00  IRQ: 000?   ";
//"012345678901234567890123456789012345678901234567890"
//              15      23          35           48

MSGTABLE  ProductIDMsgParm = { MSG_REPLACEMENT_STRING,
                               1,
                               ProductIDMsg,
                              };
static USHORT ValidPorts[] = {
//static USHORT ValidPorts[16*4];// = {
                               0x300,
                               0x310,  // Zeos ships at 310 default
                               0x320,  // Micron ships at 320 default (used to be 300)
//                             0x360,
//                             0x390,
                               0x230,  // sb16-MCD new address
                               0x630,  // reveal SC400 address
                               0x340,
                             };
#define PortChoices sizeof(ValidPorts)/sizeof(USHORT)

static UCHAR charstring[]="0123456789ABCDEF";

   int   Active_Counter = 0 ;
   static PIORB Head=0L, Tail=0L ; // Pointers to request packets queue management

//ULONG DevHlp=0;
ULONG Device_Help=0;
PFN             RM_Help0               = 0L;  /*VPNP*/
PFN             RM_Help3               = 0L;  /*VPNP*/

DRIVERSTRUCT DriverStruct =
{
   "MITFX001.ADD",                           /* DrvrName                */
   "Mitsumi CDROM Driver",                   /* DrvrDescript            */
   "IBM OS/2",                               /* VendorName              */
   CMVERSION_MAJOR,                          /* MajorVer                */
   CMVERSION_MINOR,                          /* MinorVer                */
   1994,9,1,                                 /* Date                    */
   0,                                        /* DrvrFlags               */
   DRT_ADDDM,                                /* DrvrType                */
   DRS_ADD,                                  /* DrvrSubType             */
   NULL                                      /* DrvrCallback            */
};


/*----------------------------------------------*/
/* Adapter Description                          */    /*VPNP*/
/*----------------------------------------------*/
ADAPTERSTRUCT AdapterStruct =
{
  "Mitsumi_0 CDROM Controller",      /* AdaptDescriptName; */
  0,                                 /* AdaptFlags;        */
  AS_BASE_MSD,                       /* BaseType;          */
  AS_SUB_OTHER,                      /* SubType;           */
  AS_INTF_GENERIC,                   /* InterfaceType;     */
  AS_HOSTBUS_ISA,                    /* HostBusType;       */
  AS_BUSWIDTH_16BIT,                 /* HostBusWidth;      */
  NULL,                              /* pAdjunctList;      */
  NULL                               /* reserved           */
};

/*----------------------------------------------*/
/* Device Description                          */    /*VPNP*/
/*----------------------------------------------*/

DEVICESTRUCT DevStruct =
{
   "CDROM Drive",       /* DevDescriptName; */
   DS_REMOVEABLE_MEDIA, /* DevFlags;     */
   DS_TYPE_CDROM        /* DevFlags;        */
};
HDRIVER         hDriver               = 0L;  /*VPNP*/
HADAPTER        hAdapter=0;
HDEVICE         hDevice=0;
USHORT          RMFlags               = 0;   /*VPNP*/


UCHAR            ResourceBuf[sizeof(AHRESOURCE)+sizeof(HRESOURCE)*2];  /*VPNP*/
PAHRESOURCE      pResourceList = (PAHRESOURCE) &ResourceBuf;         /*VPNP*/
extern void EnableASIC(USHORT Port, USHORT IRQ,USHORT type,USHORT CardPort);
extern BOOL near PortValid(void);

/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  CDStrat1                                                 *
 *                                                                            *
 * DESCRIPTIVE NAME:  Strategy 1 entry point                                  *
 *                                                                            *
 * FUNCTION:  This routine processes request "Device Driver Request Packets". *
 *            Only, Initialize Base Request is accepted.  All other requests  *
 *            are rejected.                                                   *
 *                                                                            *
 * NOTES:  This routine runs in protect mode only as a single thread          *
 *         at level 3 with IOPL.                                              *
 *                                                                            *
 * INPUT:  ES:BX contain pointer to request packet.                           *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
 VOID FAR CDStrat1()
 {
 PRPH pRPH;                   // Pointer to RPH (Request Packet Header)
 USHORT        Cmd;           // Local variable

 _asm { mov word ptr pRPH[0], bx       //  pRPH is initialize to
        mov word ptr pRPH[2], es };    //  ES:BX passed from the kernel
 Cmd = pRPH->Cmd;
 if (Cmd == CMDInitBase)
   DriveInit( (PRPINITIN) pRPH );
 else
   pRPH->Status = STATUS_ERR_UNKCMD;
 }

/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  ProcessReq                                               *
 *                                                                            *
 * DESCRIPTIVE NAME:  Process IORB Requests                                   *
 *                                                                            *
 * FUNCTION: This routine receives the I/O Request Blocks to process          *
 *           from the Queing entrypoint                                       *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
VOID ProcessReq( PIORBH pIORB )
 {
 PSCSI_STATUS_BLOCK pStatus;
 BOOL    Status = TRUE;
 USHORT  CmdCode;
 USHORT  SubCmd;

        CmdCode=pIORB->CommandCode;
        SubCmd =pIORB->CommandModifier;

        switch(CmdCode)
          {
          case  IOCC_CONFIGURATION     :
            switch(SubCmd)
              {
              case  IOCM_GET_DEVICE_TABLE:
                Build_Device_Table((PIORB_CONFIGURATION) pIORB);
                break;
              case  IOCM_COMPLETE_INIT:
                break;
              default:
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              } /* end switch */
            break;
          case IOCC_UNIT_CONTROL      :
            switch(SubCmd)
              {
              case IOCM_ALLOCATE_UNIT  :
                if(!Allocated)
                  {
                  Allocated=TRUE;
                  } /* end if */
                else
                  {
                  IORB_CmdErr(IOERR_UNIT_ALLOCATED, pIORB,0,0);
                  } /* end else */
                break;
              case IOCM_DEALLOCATE_UNIT:
                if(Allocated)
                  {
                  Allocated=FALSE;
                  } /* end if */
                else
                  {
                  IORB_CmdErr(IOERR_UNIT_NOT_ALLOCATED, pIORB,0,0);
                  } /* end else */
                break;
              case IOCM_CHANGE_UNITINFO:
                // not used
                if(!Allocated)
                  {
                  IORB_CmdErr(IOERR_UNIT_NOT_ALLOCATED, pIORB,0,0);
                  } /* end if */
                break;
              default:
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              } /* end switch */
            break;
          case IOCC_GEOMETRY          :
            switch(SubCmd)
              {
              case IOCM_GET_MEDIA_GEOMETRY :
              case IOCM_GET_DEVICE_GEOMETRY:
                if(Allocated)
                  {
                  if(!GetGeometry(SubCmd,(PIORB_GEOMETRY)pIORB))
                    IORB_CmdErr(IOERR_UNIT_NOT_READY, pIORB,SCSI_SK_NOTRDY,0);
                  } /* end if */
                else
                  {
                  IORB_CmdErr(IOERR_UNIT_NOT_ALLOCATED, pIORB,0,0);
                  } /* end else */
                break;
              case IOCM_SET_MEDIA_GEOMETRY :
              case IOCM_SET_LOGICAL_GEOMETRY:
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              default:
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              } /* end switch */
            break;
          case IOCC_EXECUTE_IO        :
            switch(SubCmd)
              {
              case IOCM_READ          :
                // currently not used
              case IOCM_READ_VERIFY   :
                // currently not used
              case IOCM_READ_PREFETCH :
                // currently not used
              case IOCM_WRITE         :
              case IOCM_WRITE_VERIFY  :
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              default:
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              } /* end switch */
            break;
          case IOCC_FORMAT            :
            // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
            break;
          case IOCC_UNIT_STATUS       :
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
            break;
          case IOCC_DEVICE_CONTROL    :
            if(SubCmd!=IOCM_ABORT)
              {
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
              } /* end else */
            // error
            break;
          case IOCC_ADAPTER_PASSTHRU  :
            switch(SubCmd)
              {
              case IOCM_EXECUTE_SCB :
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              case IOCM_EXECUTE_CDB:
                if(Allocated)
                  {
                  pIORB->Status &= (~IORB_STATUSBLOCK_AVAIL);
                  if(pIORB->RequestControl & IORB_REQ_STATUSBLOCK)
                    {
                    pStatus=(PSCSI_STATUS_BLOCK)MAKEP(SELECTOROF(pIORB),pIORB->pStatusBlock);
                    pStatus->Flags &= ~STATUS_SENSEDATA_VALID;
                    pStatus->SenseData->ErrCode_Valid = 0;           // ?????
                    pStatus->SenseData->SenseKey  = 0;
                    pStatus->SenseData->AddSenseCode=0;
                    } /* end if */
                  ProcessCDB(pIORB);
                  }
                else
                  {
                  IORB_CmdErr(IOERR_UNIT_NOT_ALLOCATED, pIORB,0,0);
                  } /* end else */
                break;
              default:
                // error
                IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
                break;
              } /* end switch */
            break;
          default:
            IORB_CmdErr(IOERR_CMD_NOT_SUPPORTED, pIORB,SCSI_SK_ILLEGALREQ,ASC_INVALID_COMMAND_OPCODE);
            // error
            break;
          } /* end switch */

        // set the IORB as Done processing

        pIORB->Status |= IORB_DONE;

        // if there is an Asyn post routine, call it now

        if (pIORB->RequestControl & IORB_ASYNC_POST)
          pIORB->NotifyAddress(pIORB);

 }
/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  IORB_CmdErr                                              *
 *                                                                            *
 * DESCRIPTIVE NAME:  IORB error completion                                   *
 *                                                                            *
 * FUNCTION:  This routine sets the error code into the IORB header, then     *
 *            calls ThisIORB_Complete with error completion status.           *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
 VOID  IORB_CmdErr (USHORT ErrCode, PIORBH pIORB, USHORT Sense_key, USHORT AdditionalSense)
 {

 PSCSI_STATUS_BLOCK pStatus;

  if(pIORB)
    {
    pIORB->ErrorCode = ErrCode;
    pIORB->Status |= IORB_ERROR;
    if(pIORB->RequestControl & IORB_REQ_STATUSBLOCK)
      {
      pIORB->Status |= IORB_STATUSBLOCK_AVAIL;
      pStatus=(PSCSI_STATUS_BLOCK)MAKEP(SELECTOROF(pIORB),pIORB->pStatusBlock);

      pStatus->Flags |= STATUS_SENSEDATA_VALID;
      pStatus->SenseData->ErrCode_Valid = 0x70;           // ?????
      pStatus->SenseData->SenseKey  = Sense_key;
      pStatus->SenseData->AddSenseCode=AdditionalSense;
      } /* end if */
    } /* end if */
 return;
 }

/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  Build_Device_Table                                       *
 *                                                                            *
 * DESCRIPTIVE NAME:  Build Adapter information table                         *
 *                                                                            *
 * FUNCTION:  This routine fills the DEVICETABLE, ADAPTERINFO and UNITINFO    *
 *            tables pointed to by the IORB.  All current data is compiled    *
 *            data tables.                                                    *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
 VOID Build_Device_Table(PIORB_CONFIGURATION pIORB)
 {
 DEVICETABLE far *pDEVT = pIORB->pDeviceTable;
 PADAPTERINFO     pADAPT;
 PUNITINFO        pUNIT;
 INT              A;
 USHORT           FlagMask = (UF_NOSCSI_SUPT + UF_CHANGELINE + UF_REMOVABLE | UF_NODASD_SUPT );

 pIORB->DeviceTableLen = (sizeof (UNITINFO) );
 pIORB->DeviceTableLen += sizeof (ADAPTERINFO) + sizeof (DEVICETABLE);
 pDEVT->ADDLevelMajor   = ADD_LEVEL_MAJOR;
 pDEVT->ADDLevelMinor   = ADD_LEVEL_MINOR;
 pDEVT->ADDHandle       = DeviceHandle;
 pDEVT->TotalAdapters   = 1;
 pDEVT->pAdapter[0] = (NPADAPTERINFO) (OFFSETOF (pIORB->pDeviceTable) +
                                       sizeof (DEVICETABLE));
 pADAPT = MAKEP (SELECTOROF (pIORB->pDeviceTable), pDEVT->pAdapter[0]);
 pUNIT  = &(pADAPT->UnitInfo[0]);
 for (A = 0; A < 15; A++)
   {
   pADAPT->AdapterName[A] = Adapter[A];
   } ;
 pADAPT->AdapterUnits   = 1;
 pADAPT->AdapterDevBus  = AI_DEVBUS_NONSCSI_CDROM;
 pADAPT->AdapterIOAccess = AI_IOACCESS_PIO;
 pADAPT->AdapterHostBus  = AI_HOSTBUS_ISA | AI_BUSWIDTH_8BIT;
 pADAPT->AdapterSCSITargetID = 0;
 pADAPT->AdapterSCSILUN = 0;
 pADAPT->AdapterFlags  = 0;
 pADAPT->MaxHWSGList  = 0;
 pADAPT->MaxCDBTransferLength = 0;
 pUNIT->AdapterIndex = 0;
 pUNIT->UnitIndex = 0;
 pUNIT->UnitFlags = FlagMask;
 pUNIT->UnitHandle = 0;
 pUNIT->UnitType = UIB_TYPE_CDROM;
 pUNIT->QueuingCount = 1;
 pUNIT->FilterADDHandle = 0;
 return;
 }

/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  GetGeometry                                              *
 *                                                                            *
 * DESCRIPTIVE NAME:  Return Geometry to IORB sender                          *
 *                                                                            *
 * FUNCTION:   This routine stores the geometry for either the physical       *
 *             device or the current media as requested.                      *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
 BOOL GetGeometry(USHORT SubCmd, PIORB_GEOMETRY pIORB)
 {
 CDROMSTAT Data;
 BOOL rc=FALSE;
 USHORT Status;

   pIORB->pGeometry->TotalSectors    = DefaultDiscSize; // set default
   pIORB->pGeometry->BytesPerSector  = 2048;            // set default

   DevHelp_RAS( 175 ,0 , 0, NULL);
   if(!ReadyHardware((PIORB)pIORB,SCSI_READ_CAPACITY))
     {
     Status=DriveCommand(ReadToc,NULL,NULL,&Data,sizeof(Data.TOC));
     if( (!DOOROPEN(Status)) && (!COMMANDCHECK(Status)) )
       {
       pIORB->pGeometry->TotalSectors    = (((BCD2Bin(Data.TOC.LeadoutMin)*60)+BCD2Bin(Data.TOC.LeadoutSec))*75)+BCD2Bin(Data.TOC.LeadoutFrame);
        // MAKEULONG(MAKEUSHORT(BCD2Bin(Data.TOC.LeadoutFrame),MAKEUSHORT(BCD2Bin(Data.TOC.LeadoutSec),BCD2Bin(Data.TOC.LeadoutMin)));

       pIORB->pGeometry->BytesPerSector  = 2048;

       pIORB->pGeometry->NumHeads        = 1;
       pIORB->pGeometry->SectorsPerTrack = 0x1ff;
       pIORB->pGeometry->TotalCylinders  = 0;
       pIORB->GeometryLen = sizeof (GEOMETRY);
       DevHelp_RAS( 176 ,0 , 0, NULL);
       rc=TRUE;
       } /* end if */
     } /* end if */
     DevHelp_RAS( 177 ,0 , 0, NULL);
 return rc;
 }

/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  DriveInit                                                *
 *                                                                            *
 * DESCRIPTIVE NAME: Soundblaster Pro Driver Init                             *
 *                                                                            *
 * FUNCTION: This procedure initializes the device driver.                    *
 *                                                                            *
 *           Installation will fail if there are no SBPro detected            *
 *                                                                            *
 *           This routine runs in protect mode only as a single thread        *
 *           at CPL = 0.                                                      *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
 VOID DriveInit(PRPINITIN pRPH )
 {
 PRPINITOUT pRPO;
 extern UCHAR  near last_data_byte;
 extern VOID last_code_byte();
 USHORT i,l,temp,state,PortScan,Attachment=0,AttachmentPort;
 PDDD_PARM_LIST Parmlist=(PDDD_PARM_LIST)pRPH->InitArgs;
 PSZ Parms=MAKEP(SELECTOROF(Parmlist),Parmlist->cmd_line_args);
 PMACHINE_CONFIG_INFO pMch=MAKEP(SELECTOROF(Parmlist),Parmlist->machine_config_table);
 BOOL Verbose=FALSE;
 PUCHAR p;
 UCHAR a1,a2,a3;
 struct DevClassTableStruc far *pdevs;
 RESOURCESTRUCT Resource;
 ADJUNCT AdjunctData;

 PortScan=0;            // start at first array entry

// DevHlp = (ULONG)pRPH->DevHlpEP;
 Device_Help = (ULONG)pRPH->DevHlpEP;

 pRPO = (PRPINITOUT) pRPH;

// for(i=0,l=0x300; i<PortChoices;i++,l+=4 )           // lets scan all the ports
//   {
//   ValidPorts[i]=l;
//   } /* end for */

 i=strlen(Parms);
 for(l=0; l<i; l++)     // lower case the entire string
   {
   Parms[l]|=0x20;
   } /* end for */

 for(;Parms && *Parms ; )               // loop thru parms
   {
   if(*Parms=='/')                      // did we find a slash?
     {
     if(!memcmp(Parms,"/e",2))          // read status early?
       {
       Parms+=2;                        // skip over
       ReadStatusLate=FALSE;            // set flag for pre-read status
       } /* end if */
     else if(!memcmp(Parms,"/v",2))     // verbose message request?
       {
       Parms+=2;                        // skip over
       Verbose=TRUE;                    // set flag verbose mode
       } /* end if */
#if defined(TRACE) | defined(TRACE1)
     else if(!memcmp(Parms,"/t",2))     // enable tracing?
       {
       Parms+=2;                        // skip over
       traceflag=TRUE;                  // enable tracing
       } /* end if */
#endif
     else if(!memcmp(Parms,"/i:",3))    // is it the one we want?
       {
       if((i=strlen(Parms))>=4)         // still enough chars to process?
         {
         Parms+=3;                      // skip over /I:
         if(Parms[1]>='0' && Parms[1]<='9')     // two byte irq number?
           {
           temp= ((Parms[0] - '0') *10 ) + // get the irq specified
                  (Parms[1] - '0')  ;
           Parms+=2;                       // skip over xx irqnum
           } /* end if */
         else                              // single byte irq number
           {
           temp= (Parms[0] - '0');         // get the irq specified
           Parms+=1;                       // skip over x irqnum
           } /* end else */
         switch(temp)                   // make sure its one of our choices
           {
           case 2:
             IrqMode=Irq9;                  // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 3:
             IrqMode=Irq3;                  // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 5:
             IrqMode=Irq5;                  // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 9:
             IrqMode=Irq9;                  // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 10:
             IrqMode=Irq10;                 // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 11:
             IrqMode=Irq11;                 // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 12:
             IrqMode=Irq12;                 // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           case 15:
             IrqMode=Irq15;                 // set adapter pattern
             IrqNum=temp;                   // valid IRQnumber
             break;
           default: //     irq number
             IrqMode=0;
             IrqNum=0;
             break;
           } /* end switch */
         }
       }
     else if(!memcmp(Parms,"/p:",3))         // is it the one we want?
       {
       if(strlen(Parms)>=6)             // still enough chars to process?
         {
         Parms+=3;                      // skip over /p:
         a1=Parms[0]-(((Parms[0] & 0x70) == 0x30)?48:87);
         a2=Parms[1]-(((Parms[1] & 0x70) == 0x30)?48:87);
         a3=Parms[2]-(((Parms[2] & 0x70) == 0x30)?48:87);
         temp= (a1*256 ) + (a2*16) + (a3)  ;
         Parms+=3;                      // skip over address

// Remove address range verification to allow the sound blaster with p:230
//
//       if(temp>=0x300 && temp<=0x3fc && ((temp & 3) ==0))
//         {
           ValidPorts[PortScan=(PortChoices-1)]=temp; // use only last entry
//         } /* end if */
//       else
//         {
//         PortScan=PortChoices;              // past maximum
//         } /* end else */
       } /* end if */
       else                             // /P: specified, but not enough bytes left
         {
         PortScan=PortChoices;                // past maximum
         }
       } /* end if */
     else if(!memcmp(Parms,"/at:",4))   // is it the one we want?
       {
       if(strlen(Parms)>=6)             // still enough chars to process?
         {
         Parms+=4;                      // skip over /p:
         Attachment+=(Parms[0] -0x30)*10;
         Attachment+=(Parms[1] -0x30);
         Parms+=2;
         switch(Attachment)
           {
           case 5:
             break;
           case 6:
             if(Parms[0]==',')
               {
               Parms++;
               a1=Parms[0]-(((Parms[0] & 0x70) == 0x30)?48:87);
               a2=Parms[1]-(((Parms[1] & 0x70) == 0x30)?48:87);
               a3=Parms[2]-(((Parms[2] & 0x70) == 0x30)?48:87);
               AttachmentPort=(a1*256 ) + (a2*16) + (a3)  ;
               Parms+=3;                      // skip over address
               }
             else
               {
               AttachmentPort=0x220;
               }
             break;
           default:
             Attachment=0;
             break;
           }
         } /* end if */
       else                             // /P: specified, but not enough bytes left
         {
         PortScan=PortChoices;                // past maximum
         }
       } /* end if */
     else
       Parms++;
     } /* end if */
   else
     Parms++;
   } /* end for */

 if(!(pMch->BusInfo & BUSINFO_MCA))             // only if not MCA
   {
   RMCreateDriver(&DriverStruct,
                  &hDriver);
   PGinfo  = (PGINFOSEG)DevHelp_GetDosVar(1);   // pointer to global info segment
   ScaleClock((UCHAR)(PGinfo->uchMinorVersion?pMch->CpuInfo:I386)); // say 386 if not 2.1 or above
   DevHelp_RAS( 170 ,0 , sizeof(PGinfo->msecs),&PGinfo->msecs);

#if 0                                           // resource manager is supposed to handle this
   if(PortScan==0)                              // nothing selected, scan all
     {
     if(pdevs = (struct DevClassTableStruc far *)DevHelp_GetDosVar(DHGETDOSV_DEVICECLASSTABLE))
       {
                                                // pointer to add device table
                                                // make sure we don't collide
                                                // with Adaptec 152x adapter
       for(i=0;i<pdevs->DCCount ;i++ )          // spin thru all adapters
         {
         if(!memcmp(pdevs->DCTableEntries[i].DCName,"AHA1520.ADD",11))
           {
           ValidPorts[PortChoices-1]=ValidPorts[0];
           break;
           } /* end if */
         } /* end for */
       } /* end if */
     } /* end if */
#endif

   for(; PortScan<PortChoices; PortScan++)      // loop thru array of addresses
     {
     Resource.ResourceType           = RS_TYPE_IO;
     Resource.IOResource.BaseIOPort = ValidPorts[PortScan];
     Resource.IOResource.NumIOPorts = 3;                        // what is this???
     Resource.IOResource.IOFlags    = RS_IO_EXCLUSIVE;
     if (ValidPorts[PortScan]>0x3ff)
       {
       Resource.IOResource.IOAddressLines = 16;
       }
     else
       {
       Resource.IOResource.IOAddressLines = 10;
       } /* endif */

     pResourceList->NumResource = 1;
     pResourceList->hResource[0] = 0L;                          /*VPNP*/
     pResourceList->hResource[1] = 0L;                          /*VPNP*/
     if(!RMAllocResource( hDriver, &pResourceList->hResource[0], &Resource ))
       {
       if(Attachment)
         EnableASIC(ValidPorts[PortScan],IrqNum,Attachment,AttachmentPort);  // enable Orchid or Cardinal ASIC
       AdapterBase=ValidPorts[PortScan];          // get base address
       StatusRegister=AdapterBase+1,              // status register address
       DataStatusRegister=AdapterBase+2;          // data status register address
       if(PortValid() && ChkBaseAddx())           // check for adapter presence
         {                                        // found adapter
         DevHelp_RAS( 170 ,AdapterBase , sizeof(PGinfo->msecs),&PGinfo->msecs);
         break;
         }
       else                                       // no adapter found at this address
         {
         RMDeallocResource( hDriver, pResourceList->hResource[0]);
         DevHelp_RAS( 171 ,AdapterBase , sizeof(PGinfo->msecs),&PGinfo->msecs);
         }
       }
     } /* end for */
   } /* end if */
 else
   {
   PortScan=PortChoices;                // force no loading on MCA machines
   } /* end else */

 if(PortScan==PortChoices)              // if over max entries to search thru
   {                                    // adapter wasn't found
   pRPO->CodeEnd = 0;
   pRPO->DataEnd = 0;
   pRPO->rph.Status = STATUS_QUIET_FAIL;
   DevHelp_RAS( 174 ,0, 0, NULL);
   RMDestroyDriver(hDriver);            // remove Resource manager definition
   for(i=0; i<sizeof(ProductIDMsg);i++ )
     {
     if(ProductIDMsg[i]=='?')
       {
       strcpy(&ProductIDMsg[i],"No units detected.");
       break;
       } /* end if */
     } /* end for */
   if(Verbose)
     {
     DevHelp_DisplayMsg(&ProductIDMsgParm);
     } /* end if */
   }
 else
   {
   if(ChkDrive())
     {
     DevHelp_RAS( 172 ,0 , 0, NULL);
     }
   else
      DevHelp_RAS( 173 ,0, 0,NULL);
   RMCreateAdapter( hDriver, &hAdapter, &AdapterStruct, (HDEVICE)NULL, (PAHRESOURCE)pResourceList );
   pRPO->CodeEnd = (USHORT)last_code_byte;      // set our code end
   pRPO->DataEnd = (USHORT)&last_data_byte;     // and data

   YieldFlag=DevHelp_GetDosVar(7);              // get address of yield flag
                                                // register our ADD
   DeviceHandle=DevHelp_RegisterDeviceClass(Adapter, (PVOID)IORBEntry, 0, 1);
   pRPO->rph.Status = 0x100;
   for(i=0,state=0; i<sizeof(ProductIDMsg);i++ )
     {
     if(ProductIDMsg[i]=='?')
       {
       switch(state)
         {
         case 0:
           ProductIDMsg[i]='M';
           DevStruct.DevDescriptName=&ProductIDMsg[i];
           state=1;
           break;
         case 1:
           switch(wDrvVer[0])
             {
             case 'M':
               if(Lu002)
                 {
                 p="LU002S";
                 } /* end if */
               else
                 {
                 p="LU005S";
                 } /* end else */
               break;
             case 'F':
               p="FX001";
               break;
             case 'D':
               p="FX001D";
               break;
             default:
               p="unkown";
               break;
             } /* end switch */
           memcpy(&ProductIDMsg[i],p,strlen(p));
           state=2;
           break;
         case 2:
           memcpy(&ProductIDMsg[i],wDrvVer,strlen(wDrvVer));
           p=&ProductIDMsg[i]+strlen(wDrvVer);
           state=3;
           break;
         case 3:
           ProductIDMsg[i++]=charstring[(AdapterBase / 256)];
           ProductIDMsg[i++]=charstring[((AdapterBase & 250)/16)];
           ProductIDMsg[i]=charstring[(AdapterBase & 15)];
           state=4;
           break;
         case 4:
           if(IrqNum)
             {
             ProductIDMsg[i]=charstring[IrqNum];
             } /* end if */
           else
             {
             strcpy(&ProductIDMsg[i-3],"Polled");
             } /* end else */
           break;
         default:
           break;
         } /* end switch */
       } /* end if */
     } /* end for */
   if(Verbose)
     {
     DevHelp_DisplayMsg(&ProductIDMsgParm);
     } /* end if */
   *p='\0';
   memset(&AdjunctData,0,sizeof(AdjunctData));
   AdjunctData.AdjType=ADJ_ADD_UNIT;
   AdjunctData.AdjLength=sizeof(AdjunctData);
   AdjunctData.Add_Unit.ADDHandle=DeviceHandle;
   AdjunctData.Add_Unit.UnitHandle=0;
   DevStruct.pAdjunctList=&AdjunctData;

   // nasty code here. the text we were already using for the device string
   // is a static variable, modified with parms we found during install
   // so to save space, we are going to backup and PVERLAY a portion of the
   // existing message..
   // backup length of new text
   DevStruct.DevDescriptName-=strlen(devstring);
   // copy in this new        prefix...
   memcpy(DevStruct.DevDescriptName,devstring,strlen(devstring));
   // now create the device with this new modified title..
   RMCreateDevice( hDriver, &hDevice, &DevStruct, hAdapter, NULL);
   }
 return;
 }
// =========================================== //
//                                             //
// This routine will queue the request packet  //
//                                             //
// input : rp - request packet pointer to be   //
//              queued                         //
//                                             //
// ------------------------------------------- //

void QIORB( PIORBH pIORB )
{
   pIORB->pNxtIORB = 0L ;            // Clear forward link pointer

   if( Head==Tail && Head==0L )      // if nothing queued
      Head = Tail = pIORB ;          // make this one both head and tail

   else
   {
      Tail->pNxtIORB = pIORB ;       // else link this one to last
      Tail           = pIORB ;       // and make this one last in list
   }
}

// ================================================= //
//                                                   //
// This routine will dequeue the last request packet //
//                                                   //
// input : none                                      //
//                                                   //
// output : last request packet in list              //
//                                                   //
// ------------------------------------------------- //

PIORBH DeQIORB( void )
{
      PIORBH pIORB ;

   if( pIORB=Head )           // if there is an entry on queue
   {
      Head = pIORB->pNxtIORB ; // unlink it

      if( Head == 0L )     // if only entry
         Tail = 0L ;       // clear tail pointer too
   }

   return pIORB ;             // return pointer to dequeued packet
}
/************************ START OF SPECIFICATIONS *****************************
 *                                                                            *
 * SUBROUTINE NAME:  IORBEntry                                                *
 *                                                                            *
 * DESCRIPTIVE NAME:  IORB Entry point                                        *
 *                                                                            *
 * FUNCTION: This routine receives the I/O Request Blocks from the device     *
 *           and filter managers.                                             *
 *                                                                            *
 ************************* END OF SPECIFICATIONS ******************************/
VOID _loadds FAR IORBEntry( PIORBH pIORB )
{
   ++Active_Counter;
   QIORB(pIORB);
   if( Active_Counter==1 )
   {
      while(Head)
        {
        ProcessReq(DeQIORB());
        } /* end while */
   }
   --Active_Counter;
}
