/*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 = CDINIT.C
 *
 * DESCRIPTIVE NAME = Initialization routines for CD-ROM Device Manager
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "infoseg.h"
#include "cdh.h"
#include "devclass.h"
#include "dskinit.h"

VOID   NEAR CD_Build_UnitCBs (void);
VOID   BuildIORB_UnitControl(USHORT, USHORT);
VOID   NEAR InitCBPool (void);
VOID   NEAR Init_Trace (void);
USHORT NEAR InitializeDevice (NPUNITCB);
VOID   NEAR GetInitParms (PRPINITIN);
VOID   NEAR GetProductID (NPUNITCB);
VOID   NEAR init_scsi_cdrom (NPUNITCB);
VOID   NEAR init_proprietary_cdrom (NPUNITCB);
VOID   NEAR init_atapi_cdrom (NPUNITCB);
VOID   NEAR init_audio_caps (NPUNITCB);
VOID   NEAR init_submit_cdb (NPUNITCB, NPIORB_CDB);

typedef struct InfoSegGDT FAR *PInfoSegGDT;
#define MSG_REPLACEMENT_STRING 1178

/*
** Init data allocated at the end of the data segment.
*/


USHORT          InitData=0;

struct vendor_id_entry vendor_id_table [] =
{
   { "IBM     ", IBM},
   { "TOSHIBA ", TOSHIBA},
   { "SONY    ", SONY},
   { "HITACHI ", HITACHI},
   { "NEC     ", NEC},
   { "TEXEL   ", TEXEL},
   { "CHINON  ", CHINON},
   { "MATSHITA", MATSHITA},                      /* PANASONIC */
   { "PIONEER ", PIONEER},
   { "LMS     ", LMS},

};

struct product_id_entry product_id_table [] =
{
   { "TOSHIBA CD-ROM XM-3301TA", TOSHIBA_3301},
   { "TOSHIBA CD-ROM XM-3401TA", TOSHIBA_3401},
   { "IBM     CDRM00101     !D", TOSHIBA_3301},  /* IBM CD-ROM II             */
   { "IBM     CDRM00201     !E", TOSHIBA_3401},  /* IBM Enhanced CD-ROM II    */
   { "IBM     CD-ROM DRIVE:XM ", TOSHIBA_3301},  /* IBM CD-ROM II for RS6000  */
   { "TOSHIBA CD-ROM DRIVE:XM ", TOSHIBA_3201}, /*2,3,4,5 = byte 0 of revision*/
   { "SONY    CD-ROM CDU-561  ", SONY_561},
   { "SONY    CD-ROM CDU-541  ", SONY_541},      /* CDU-7211, 541             */
   { "HITACHI CDR-3750        ", HITACHI_3750},
   { "HITACHI CDR-6750        ", HITACHI_6750},
   { "MATSHITACD-ROM CR-5XX   ", PANASONIC_501}, /* PANASONIC CR-501          */
   { "TEXEL   CD-ROM          ", TEXEL_3024},    /* Texel 3024,5024           */
   { "TEXEL   CD-ROM DM-XX24 K", TEXEL_3024K},   /* Texel 3024,5024 with Kodak*/
   { "PIONEER CD-ROM DRM-604X ", PIONEER_604X},  /* Pioneer DRM-604X          */
   { "CHINON  CD-ROM CDS-535  ", CHINON_535},    /* Chinon 535                */
   { "LMS     CM215           ", LMS_215},       /* LMS CM215                 */
   { "NEC     CD-ROM DRIVE:841", NEC_84_1},      /* NEC 84-1                  */
   { "NEC     CD-ROM DRIVE:260", NEC_260_17B},

};


/* Drive capabilities table */

struct drive_caps_entry drive_caps_table [] =
{
   {TOSHIBA_3301,  DCAPS_XA | DCAPS_UPC},
   {TOSHIBA_3401,  DCAPS_XA | DCAPS_CDDA | DCAPS_MULTISESSION | DCAPS_UPC},
   {SONY_561,      DCAPS_XA | DCAPS_CDDA | DCAPS_MULTISESSION | DCAPS_UPC |
                   DCAPS_UPC_IN_ASCII},
   {SONY_541,      DCAPS_XA | DCAPS_NO_ADR_RETURNED | DCAPS_UPC |
                   DCAPS_UPC_IN_BCD | DCAPS_MODE_WITH_ERR_RECOV},

   {HITACHI_3750,  0},
   {HITACHI_6750,  DCAPS_XA | DCAPS_MULTISESSION},
   {PANASONIC_501, DCAPS_UPC},
   {TEXEL_3024,    DCAPS_XA | DCAPS_NO_ADR_RETURNED | DCAPS_UPC |
                   DCAPS_UPC_IN_BCD | DCAPS_MODE_WITH_ERR_RECOV},
   {TEXEL_3024K,   DCAPS_XA | DCAPS_NO_ADR_RETURNED | DCAPS_UPC |
                   DCAPS_UPC_IN_BCD | DCAPS_MODE_WITH_ERR_RECOV |
                   DCAPS_MULTISESSION},
   {PIONEER_604X,  DCAPS_XA | DCAPS_MULTISESSION},
   {CHINON_535,    DCAPS_XA | DCAPS_MULTISESSION},
   {LMS_215,       DCAPS_XA | DCAPS_MULTISESSION},
   {NEC_84_1,      DCAPS_XA | DCAPS_MULTISESSION},
};


#define VENDOR_ID_COUNT sizeof(vendor_id_table) /         \
                         sizeof(struct vendor_id_entry)

#define PRODUCT_ID_COUNT sizeof(product_id_table) /       \
                         sizeof(struct product_id_entry)

#define DRIVE_CAPS_COUNT sizeof(drive_caps_table) /       \
                         sizeof(struct drive_caps_entry)


UCHAR           InitTimeIORB[sizeof(IORB_CDB)]={0};

#define MAX_DT_ADAPTERS  8
#define MAX_DT_UNITS     56
#define MAX_DT_SIZE (sizeof(DEVICETABLE) + ((MAX_DT_ADAPTERS-1) * 2) +        \
              (MAX_DT_ADAPTERS * (sizeof(ADAPTERINFO)-sizeof(UNITINFO))) +    \
              (MAX_DT_UNITS * sizeof(UNITINFO))  )

UCHAR           ScratchBuffer2[MAX_DT_SIZE]={0};       /*Scratch buffer */

USHORT          ReserveDriveLetters = 0;


static UCHAR HeaderIDMsg[] =                                    \
"\n\r"                                                          \
"IBM OS/2 OS2CDROM.DMD (950113)";


static UCHAR ProductIDMsg[] =                                   \
"                                ";

MSGTABLE  HeaderMsgParm = { MSG_REPLACEMENT_STRING,
                            1,
                            HeaderIDMsg,
                          };


MSGTABLE  ProductIDMsgParm = { MSG_REPLACEMENT_STRING,
                               1,
                               ProductIDMsg,
                              };


/****************************************************************************
 *
 * FUNCTION NAME = CD_DriveInit
 *
 * DESCRIPTION   = Initialization for OS2CDROM.DMD
 *
 * INPUT         = pRP           - pointer to Request Packet
 *                 pUnitCB       - pointer to Unit Control Block
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT near CD_DriveInit(pRP, pUnitCB)

PRPINITIN  pRP;
NPUNITCB   pUnitCB;
{
  USHORT     rc, i, DriveCount;
  PRPINITOUT pRPO = (PRPINITOUT) pRP;   /* Output for Init RP             */

  extern USHORT (near *Strat1Near[])();


  /*
  ** Initialize various variables
  */
  CDFlags |= CDF_INIT_TIME;             /* Turn on init time flag         */

  Device_Help = pRP->DevHlpEP;          /* Save ptr to devhelp function   */
  FirstDriveNumber = pRP->DriveNum;     /* First Drive Number             */

  pDataSeg = (PVOID) &pDataSeg;         /* Set up pointer to data segment */
  OFFSETOF(pDataSeg) = 0;

  /*
  ** Get the init parms specified on the BASEDEV= command line
  */
  GetInitParms(pRP);

  /*
  ** Save away the physical and linear addresses of our data segment
  */
  rc = DevHelp_VirtToPhys(pDataSeg, (PULONG) &ppDataSeg);

  rc = DevHelp_VirtToLin((USHORT) (SELECTOROF(pDataSeg)),
                         (ULONG) (OFFSETOF(pDataSeg)),
                         (PLIN) &plDataSeg);

  /*
  ** Put init data at end of _DATA since it'll get discarded later
  */
  PoolSize = INIT_POOL_SIZE;

  FreePoolSpace = INIT_POOL_SIZE;

  CD_Build_UnitCBs();                   /* Build unit control blocks      */

  if (NumCDROMDrives == 0)              /* If no drives, then deinstall   */
  {
     if (ReserveDriveLetters == 0)
     {
        pRPO->Unit = 0;
        pRPO->CodeEnd = 0;
        pRPO->DataEnd = 0;
        ZeroFSGS();
        if (CDFlags & CDF_QUIET)
           return(STDON + STERR + ERROR_I24_QUIET_INIT_FAIL);
        else
           return(STDON + STERR + ERROR_I24_GEN_FAILURE);
     }
     else
     {
        UnitCB_Head = 0;
        pRP->Unit = (CHAR) ReserveDriveLetters;
        pRPO->BPBArray = (PVOID) InitBPBArray;
        for (i = 0; i < ReserveDriveLetters; i++)
           InitBPBArray[i] = &DefaultBPB;

        /*
        ** Return the end of the code and data segments
        */
        pRPO->CodeEnd = (USHORT) CD_DriveInit;
        pRPO->DataEnd = (USHORT) FirstUnitCB;

        CodeBreakAddress = (USHORT) CD_DriveInit;
        DataBreakAddress = (USHORT) FirstUnitCB;
     }
  }
  else                                  /* At least 1 CD-ROM drive exists */
  {
     InitCBPool ();                     /* Initialize the Control Block pool */

     Init_Trace();                      /* Initialize tracing             */

     /*
     ** Set up the return parameters for the INIT packet.
     */
     if (ReserveDriveLetters > NumCDROMDrives)
        DriveCount = ReserveDriveLetters;
     else
        DriveCount = NumCDROMDrives;

     pRP->Unit = (CHAR) DriveCount;     /* Return number of logical drives */

     pRPO->BPBArray = (PVOID) InitBPBArray;

     for (i = 0; i < DriveCount; i++)
        InitBPBArray[i] = &DefaultBPB;

     /*
     ** Return the end of the code and data segments
     */
     pRPO->CodeEnd = (USHORT) CD_DriveInit;
     pRPO->DataEnd = (USHORT) pNextFreeCB;

     CodeBreakAddress = (USHORT) CD_DriveInit;
     DataBreakAddress = (USHORT) pNextFreeCB;

  }
  /*
  ** Dont allow another INIT command to come in
  */
  Strat1Near[CMDInit] = CmdErr;       /* Patch strat1 table to disable inits */
  Strat1Near[CMDInitBase] = CmdErr;   /* Patch strat1 table to disable inits */
  CDFlags &= ~CDF_INIT_TIME;          /* Turn off init time flag             */

  return(STDON);                      /* Done with init, so return           */

}


/****************************************************************************
 *
 * FUNCTION NAME = CD_Build_UnitCBs
 *
 * DESCRIPTION   = Build Unit Control Blocks (UnitCBs)
 *
 *                 This routine issues the GetAdapterDeviceTable command
 *                 to each Adapter Device Driver and builds the unit control
 *                 blocks from each Adapter Device Table returned.  Each unit
 *                 control block (UnitCB) represents a physical device unit
 *                 which the device manager manages.
 *
 *                 VOID CD_Build_UnitCS()
 *
 *                 NOTES: UnitCBs built for each CD-ROM target.
 *                        NumCDRomDrives = Number of UnitCBs built
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

void CD_Build_UnitCBs()

{
  USHORT                rc;
  NPUNITCB              pUnitCB;         /* Pointer to current UnitCB    */
  NPIORB_CONFIGURATION  pIORB;           /* ptr to IORB                  */
  DEVICETABLE           *pDeviceTable;   /* ptr to device table          */
  NPADAPTERINFO         pAdapterInfo;    /* near ptr to AdapterInfo      */
  USHORT                i, j, k, l;      /* Index pointers               */
  USHORT                FilterADDHandle; /* Filter Handle                */
  NPBYTE                pInquiryData;
  struct
  DevClassTableStruc far *pDriverTable;  /* ptr to registered ADD EPs    */
  VOID (FAR * DriverEP) ();              /* Driver entry point           */
  VOID (FAR * DriverEPF)();              /* Filter Driver entry point    */

  (NPUNITCB)pNextFreeCB = FirstUnitCB;   /* Next free is first unit CB   */
  UnitCB_Head = FirstUnitCB;             /* Init UnitCB head pointer     */
  pUnitCB = UnitCB_Head;                 /* Point to first UnitCB        */
  NumUnitCBs = 0;                        /* Init UnitCB count            */


  /*
  ** Get the adapter device tables for each adapter driver and create
  ** the unit control blocks (UnitCBs) from the returned tables.
  */

  rc = DevHelp_GetDOSVar((USHORT) DHGETDOSV_DEVICECLASSTABLE, 1,
                         (PPVOID) &pDriverTable);

  NumDrivers = pDriverTable->DCCount;

  pDeviceTable = (DEVICETABLE *) ScratchBuffer2;

  for (i = 0; i < NumDrivers; i++)
  {
     pIORB = (NPIORB_CONFIGURATION) InitTimeIORB;
     pIORB->iorbh.Length = sizeof(IORB_CONFIGURATION);
     pIORB->iorbh.CommandCode = IOCC_CONFIGURATION;
     pIORB->iorbh.CommandModifier = IOCM_GET_DEVICE_TABLE;
     pIORB->iorbh.Status = 0;
     pIORB->iorbh.ErrorCode = 0;
     pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
     pIORB->iorbh.NotifyAddress = &InitPost;
     pIORB->pDeviceTable = (PVOID) pDeviceTable;
     pIORB->DeviceTableLen = sizeof(ScratchBuffer2);

     OFFSETOF(DriverEP) =  pDriverTable->DCTableEntries[i].DCOffset;
     SELECTOROF(DriverEP) = pDriverTable->DCTableEntries[i].DCSelector;

     f_ZeroCB((PBYTE)pDeviceTable, sizeof(ScratchBuffer2));

     (*DriverEP) ((PVOID)(pIORB));

     while ( !(pIORB->iorbh.Status & IORB_DONE) )  /* Wait till done */
     ;

     for (j = 0; j < pDeviceTable->TotalAdapters; j++)
     {
        pAdapterInfo =  pDeviceTable->pAdapter[j];

        for (k = 0; k < pAdapterInfo->AdapterUnits; k++)
        {
           /*
           ** Only allocate DISK type devices which are not defective
           ** and which dont suppress DASD manager support.
           */
           if ( (pAdapterInfo->UnitInfo[k].UnitType == UIB_TYPE_CDROM) &&
                ! (pAdapterInfo->UnitInfo[k].UnitFlags & UF_DEFECTIVE) )
           {
              /*
              ** Allocate the unit if it's not already allocated
              ** Wait until the request comes back from ADD.
              */

              BuildIORB_UnitControl (IOCM_ALLOCATE_UNIT,
                                     pAdapterInfo->UnitInfo[k].UnitHandle);

              FilterADDHandle = pAdapterInfo->UnitInfo[k].FilterADDHandle;

              if ( !FilterADDHandle )
              {
                 (*DriverEP) ((PVOID)(pIORB));
              }
              else
              {
                 OFFSETOF(DriverEPF) =
                     pDriverTable->DCTableEntries[FilterADDHandle-1].DCOffset;
                 SELECTOROF(DriverEPF) =
                     pDriverTable->DCTableEntries[FilterADDHandle-1].DCSelector;

                 (*DriverEPF) ((PVOID)(pIORB));
              }

              while ( !(pIORB->iorbh.Status & IORB_DONE) )  /* Wait till done */
              ;

              /*
              ** If allocation succeeded then add unit to unit tables
              */
              if ( !(pIORB->iorbh.Status & IORB_ERROR) )
              {

                 pUnitCB->UnitInfo = pAdapterInfo->UnitInfo[k];

                 /*
                 ** Save the callable entry point of the adapter driver. If
                 ** the unit is being filtered, use the entry point of the
                 ** filter driver.
                 */
                 if (FilterADDHandle == 0)
                     pUnitCB->AdapterDriverEP = DriverEP;
                 else
                 {
                    OFFSETOF(pUnitCB->AdapterDriverEP)   =
                                               OFFSETOF(DriverEPF);
                    SELECTOROF(pUnitCB->AdapterDriverEP) =
                                               SELECTOROF(DriverEPF);
                 }

                 /*
                 ** Set the bus interface type.
                 */
                 if ((pAdapterInfo->AdapterDevBus & 0x00FF) ==
                                                AI_DEVBUS_NONSCSI_CDROM)
                    pUnitCB->DeviceInfo.interface_type = INTERFACE_PROPRIETARY;

                 else if ((pAdapterInfo->AdapterDevBus & 0x00FF) ==
                                                AI_DEVBUS_ST506)
                    pUnitCB->DeviceInfo.interface_type = INTERFACE_ATAPI;
                 else
                    pUnitCB->DeviceInfo.interface_type = INTERFACE_SCSI;

                 /*
                 ** Initialize device.  If failure then deallocate unit
                 */

                 if (InitializeDevice(pUnitCB) & STERR)
                 {
                    BuildIORB_UnitControl (IOCM_DEALLOCATE_UNIT,
                                     pAdapterInfo->UnitInfo[k].UnitHandle);

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

                    while ( !(pIORB->iorbh.Status & IORB_DONE) )
                    ;
                 }
                 else
                 {
                    pUnitCB->AdapterNumber = NumAdapters + j;
                    pUnitCB->MaxHWSGList = pAdapterInfo->MaxHWSGList;

                    if (pAdapterInfo->AdapterFlags & AF_HW_SCATGAT)
                       pUnitCB->Flags |= UCF_HW_SCATGAT;

                    if (pAdapterInfo->AdapterFlags & AF_16M)
                       pUnitCB->Flags |= UCF_16M;

                    if (pAdapterInfo->AdapterFlags & AF_CHS_ADDRESSING)
                       pUnitCB->Flags |= UCF_CHS_ADDRESSING;

                    /*
                    ** Display vendor, product id and revision code
                    ** if /V parm specified on command line.
                    */
                    if (CDFlags & CDF_VERBOSE)
                    {
                       if (NumUnitCBs == 0)
                          DevHelp_Save_Message( (NPBYTE) &HeaderMsgParm);

                       pInquiryData = &pUnitCB->InquiryData.vendor_id[0];

                       ProductIDMsg[0]=pUnitCB->UnitInfo.UnitSCSITargetID+0x30;

                       for (l = 0; l < 8; l++)
                          ProductIDMsg[l+2] = *(pInquiryData+l);

                       for (l = 8; l < 24; l++)
                          ProductIDMsg[l+3] = *(pInquiryData+l);

                       for (l = 24; l < 28; l++)
                          ProductIDMsg[l+4] = *(pInquiryData+l);

                       DevHelp_Save_Message( (NPBYTE) &ProductIDMsgParm);
                    }

                    /*
                    ** Increment drive count and get next one
                    */
                    pUnitCB->LogDriveNum = NumUnitCBs;
                    NumUnitCBs ++;
                    NumCDROMDrives++;
                    pUnitCB->pNextUnitCB = pUnitCB + 1;
                    pUnitCB++;


                 }
              }
           }
        }  /* end unit loop */
     }  /* end adapter loop */
     NumAdapters += pDeviceTable->TotalAdapters;
  }  /* end driver loop */

  (NPUNITCB) pNextFreeCB = pUnitCB;     /* Update next free control blk ptr */
  (pUnitCB-1)->pNextUnitCB = 0;
}



/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_UnitControl
 *
 * DESCRIPTION   = This routine build a unit control IORB.
 *
 *                 VOID Build_UnitControl(USHORT command_modifier,
 *                                                 USHORT unit_handle);
 *
 * INPUT         = command_modifier   - IORB command modifier code
 *                 unit_handle        - unit handle
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID BuildIORB_UnitControl (command_modifier, unit_handle)

USHORT     command_modifier;
USHORT     unit_handle;

{
   NPIORB_UNIT_CONTROL pIORB;

   pIORB = (NPIORB_UNIT_CONTROL) InitTimeIORB;

   pIORB->iorbh.Length = sizeof(IORB_UNIT_CONTROL);
   pIORB->iorbh.CommandCode = IOCC_UNIT_CONTROL;
   pIORB->iorbh.CommandModifier = command_modifier;
   pIORB->iorbh.Status = 0;
   pIORB->iorbh.ErrorCode = 0;
   pIORB->iorbh.UnitHandle = unit_handle;
   pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
   pIORB->iorbh.NotifyAddress = &InitPost;
   pIORB->Flags = 0;

   pIORB->pUnitInfo = 0;
   pIORB->UnitInfoLen = 0;
}



/****************************************************************************
 *
 * FUNCTION NAME = InitializeDevice
 *
 * DESCRIPTION   = initialize the target device
 *
 *                 VOID InitializeDevice(NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * OUTPUT        = USHORT          - error status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT InitializeDevice (pUnitCB)

NPUNITCB pUnitCB;
{
    USHORT        i, IORBError;                                      /*V@91985*/
    NPIORB_CDB    pIORB;
    struct Channel *channel;
    struct Audio   *audio;
    struct ModeSelectParmList NEAR *pDescriptor;
    union ADD_ReadDiskInfo_Data NEAR *pCDBData;

    audio = &pUnitCB->DeviceInfo.Audio;
    channel = &audio->channel;

    /*
    ** Issue Test Unit Ready to clear any pending check conditions. Retry
    ** up to 5 times, if still check condition, then return device error
    */
    for (i = 1; i <= 5; i++)
    {
       BuildCDB_TestUnitReady (pUnitCB, (NPIORB_CDB FAR *) &pIORB);
       init_submit_cdb (pUnitCB, pIORB);
       if ( ! (pIORB->apt.iorbh.Status & IORB_ERROR) )
          break;

       IORBError = MapSenseData ((NPIORB)pIORB);                     /*V@91985*/

       if ( (IORBError == STDON + STERR + ERROR_I24_NOT_READY) ||    /*V@91985*/
            (IORBError == STDON + STERR + ERROR_I24_SECTOR_NOT_FOUND) )
           break;                                                    /*V@91985*/

       if (i == 5)
          return (STDON + STERR + ERROR_I24_GEN_FAILURE);
    }

    /*
    ** Then issue a Request Sense Command
    */
    BuildCDB_RequestSense ( pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    init_submit_cdb (pUnitCB, pIORB);

    /*
    ** Get the inquiry data
    */
    BuildCDB_Inquiry(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    init_submit_cdb (pUnitCB, pIORB);

    /*
    ** Get the vendor and product id code from the inquiry data
    */

    GetProductID (pUnitCB);

    /*
    ** Initialize drive based on interface type
    */

    audio->capabilities = 0;

    switch (pUnitCB->DeviceInfo.interface_type)
    {
       case INTERFACE_SCSI:
          init_scsi_cdrom(pUnitCB);
          break;

       case INTERFACE_PROPRIETARY:
          init_proprietary_cdrom(pUnitCB);
          break;

       case INTERFACE_ATAPI:
          init_atapi_cdrom(pUnitCB);
          break;
    }

    /*
    ** Issue a Mode Select to set default density code to CD-ROM
    ** and default block size to 2048 bytes.
    */

    if (pUnitCB->DeviceInfo.interface_type != INTERFACE_ATAPI)
    {
       BuildCDB_ModeSelect (pUnitCB, CD_DENSITY_DEFAULT, 2048,
                                                  (NPIORB_CDB FAR *) &pIORB);
       init_submit_cdb (pUnitCB, pIORB);
    }

    /*
    ** Initialize device info fields
    */
    pUnitCB->DeviceInfo.current_block_size = 2048;
    pUnitCB->DeviceInfo.raw_block_size = 0;
    pUnitCB->DeviceInfo.current_density = CD_DENSITY_DEFAULT;
    pUnitCB->DeviceInfo.disk_density = CD_DENSITY_DEFAULT;

    pUnitCB->DeviceInfo.playing = FALSE;

    pDescriptor = (struct ModeSelectParmList NEAR *) &(pIORB->CDB_data);

    /*
    ** Initialize the audio channels to stereo and max volume.
    */
    channel->input_0  = 0;
    channel->volume_0 = 0xFF;
    channel->input_1  = 1;
    channel->volume_1 = 0xFF;

    BuildCDB_AudioControl(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    init_submit_cdb (pUnitCB, pIORB);

    /*
    ** If no error, then indicate audio supported on this device
    */
    if ( ! (pIORB->apt.iorbh.Status & IORB_ERROR) )
       pUnitCB->Flags |= UCF_AUDIO_SUPPORTED;


   return (STDON);
}

/****************************************************************************
 *
 * FUNCTION NAME = init_scsi_cdrom
 *
 * DESCRIPTION   = initialize scsi cd-rom drive
 *
 *                 VOID init_scsi_cdrom  (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * OUTPUT        = USHORT          - error status word
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID init_scsi_cdrom (pUnitCB)

NPUNITCB pUnitCB;
{
    USHORT n;
    struct Audio   *audio;

    audio = &pUnitCB->DeviceInfo.Audio;

    /*
    ** Set driver capability bits from drive capabilites table
    */

    for (n = 0; n < DRIVE_CAPS_COUNT; n++)
    {
       if (pUnitCB->DeviceInfo.product_id_code ==
                               drive_caps_table[n].product_id_code)
       {
          audio->capabilities |= drive_caps_table[n].capabilities;
          break;
       }
    }

    /*
    ** Initialize the audio capabilities
    */

    init_audio_caps(pUnitCB);

    /*
    ** Some specialized bit twiddling for some SCSI drives.
    **
    ** Hitachi and Texel return Not Ready for Mode Selects when no
    ** media present, so set audio bits directly.
    */

    switch (pUnitCB->DeviceInfo.vendor_id_code)
    {
       case HITACHI:
          audio->capabilities |= (DCAPS_NO_AUDIO_CTRL_DURING_PLAY |
                                  DCAPS_NO_CHANNEL_SWAP |
                                  DCAPS_INDEPENDENT_VOLUME_LEVELS);
          break;

       case TEXEL:
          audio->capabilities |= (DCAPS_MUTE | DCAPS_INDEPENDENT_VOLUME_LEVELS);
          break;

       case IBM:
          if (pUnitCB->DeviceInfo.product_id_code == TOSHIBA_3301)
             audio->capabilities |= DCAPS_CDDA;
          break;
    }
}

/****************************************************************************
 *
 * FUNCTION NAME = init_proprietary_cdrom
 *
 * DESCRIPTION   = initialize proprietary CD-ROM drive
 *
 *                 VOID init_proprietary_cdrom (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * OUTPUT
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID init_proprietary_cdrom (pUnitCB)

NPUNITCB pUnitCB;
{
    NPIORB_CDB    pIORB;
    union ADD_ReadDiskInfo_Data NEAR *pCDBData;
    struct Audio   *audio;

    audio = &pUnitCB->DeviceInfo.Audio;


    /*
    ** Issue ReadDriveInfo command to get the capabilites of the drive.
    */

    ADD_BuildCDB_ReadDiskInfo (pUnitCB, TYPE_CAPABILITIES_INFO,
                                    (NPIORB_CDB FAR *) &pIORB);

    init_submit_cdb (pUnitCB, pIORB);

    if ( !(pIORB->apt.iorbh.Status & IORB_ERROR) )
    {
       pCDBData = (union ADD_ReadDiskInfo_Data NEAR *) pIORB->CDB_data;

       if (pCDBData->capabilities.read_capabilities & CAP_MULTISESSION)
           audio->capabilities |= DCAPS_MULTISESSION;

       if (pCDBData->capabilities.read_capabilities & CAP_MODE2_FORM2)
           audio->capabilities |= DCAPS_XA;

       if (pCDBData->capabilities.read_capabilities & CAP_CDDA)
           audio->capabilities |= DCAPS_CDDA;
    }

    /*
    ** Initialize the audio capabilities
    */

    init_audio_caps(pUnitCB);
}


/****************************************************************************
 *
 * FUNCTION NAME = init_atapi_cdrom
 *
 * DESCRIPTION   = initialize atapi cd-rom drive
 *
 *                 VOID init_atapi_cdrom  (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID init_atapi_cdrom (pUnitCB)

NPUNITCB pUnitCB;
{
    NPIORB_CDB    pIORB;
    struct CapabilitiesParmList_10 NEAR *pCDBData;
    struct Audio   *audio;

    audio = &pUnitCB->DeviceInfo.Audio;

    /*
    ** Issue Mode Sense to read the CD-ROM capabilites page.
    */

    BuildCDB_SenseCapabilities (pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    init_submit_cdb (pUnitCB, pIORB);

    /*
    ** Set the capability bits in the unit control block
    */

    if ( !(pIORB->apt.iorbh.Status & IORB_ERROR) )
    {
       pCDBData = (struct CapabilitiesParmList_10 NEAR *) pIORB->CDB_data;

       if (pCDBData->CapPage.capabilities & CP_MULTISESSION)
           audio->capabilities |= DCAPS_MULTISESSION;

       if (pCDBData->CapPage.capabilities & CP_MODE2_FORM2)
           audio->capabilities |= DCAPS_XA;

       if (pCDBData->CapPage.capabilities & CP_CDDA)
           audio->capabilities |= DCAPS_CDDA;

       if (pCDBData->CapPage.capabilities & CP_INDEPENDENT_VOLUME_LEVELS)
           audio->capabilities |= DCAPS_INDEPENDENT_VOLUME_LEVELS;

       if ( (pCDBData->CapPage.num_volume_levels.usbytes.byte_0 != 0) ||
            (pCDBData->CapPage.num_volume_levels.usbytes.byte_1 > 2))
           audio->capabilities |= DCAPS_VARIABLE_VOLUME;

       audio->capabilities |= DCAPS_MUTE;

       if (pCDBData->CapPage.capabilities & CP_UPC)
       {
           audio->capabilities |= DCAPS_UPC;
           audio->capabilities |= DCAPS_UPC_IN_ASCII;
       }
    }
}

/****************************************************************************
 *
 * FUNCTION NAME = init_audio_caps
 *
 * DESCRIPTION   = initialize audio capabilities
 *
 *                 VOID init_audio_caps (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID init_audio_caps(pUnitCB)

NPUNITCB pUnitCB;
{

    NPIORB_CDB    pIORB;
    struct ModeSelectParmList NEAR *pDescriptor;
    struct Channel *channel;
    struct Audio   *audio;

    audio = &pUnitCB->DeviceInfo.Audio;
    channel = &audio->channel;

    /*
    **
    ** Determine audio capabilities of the drive by issuing Mode
    ** Selects with different audio control page parameters.
    **
    */

    /*
    ** Determine if the drive supports variable volume control.
    */

    audio->capabilities |= DCAPS_VARIABLE_VOLUME;
    BuildCDB_AudioControl(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    pDescriptor->Descriptors.audio_control.output0_select = 1;
    pDescriptor->Descriptors.audio_control.output0_volume = 0xAA;
    pDescriptor->Descriptors.audio_control.output1_select = 2;
    pDescriptor->Descriptors.audio_control.output1_volume = 0xAA;

    init_submit_cdb (pUnitCB, pIORB);

    if (pIORB->apt.iorbh.Status & IORB_ERROR)
       audio->capabilities &= ~DCAPS_VARIABLE_VOLUME;

    /*
    ** Determine if the drive supports setting the channel select to mute
    */
    audio->capabilities |= DCAPS_MUTE;
    BuildCDB_AudioControl(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    pDescriptor->Descriptors.audio_control.output0_select = 0;
    pDescriptor->Descriptors.audio_control.output0_volume = 0xFF;
    pDescriptor->Descriptors.audio_control.output1_select = 0;
    pDescriptor->Descriptors.audio_control.output1_volume = 0xFF;

    init_submit_cdb (pUnitCB, pIORB);

    if (pIORB->apt.iorbh.Status & IORB_ERROR)
       audio->capabilities &= ~DCAPS_MUTE;

    /*
    ** Determine if the drive supports setting the volume level for
    ** channel 1 to be different from the volume level for channel 2
    */
    audio->capabilities |= DCAPS_INDEPENDENT_VOLUME_LEVELS;
    BuildCDB_AudioControl(pUnitCB, (NPIORB_CDB FAR *) &pIORB);
    pDescriptor->Descriptors.audio_control.output0_select = 1;
    pDescriptor->Descriptors.audio_control.output0_volume = 0x00;
    pDescriptor->Descriptors.audio_control.output1_select = 2;
    pDescriptor->Descriptors.audio_control.output1_volume = 0xFF;

    init_submit_cdb (pUnitCB, pIORB);

    if (pIORB->apt.iorbh.Status & IORB_ERROR)
       audio->capabilities &= ~DCAPS_INDEPENDENT_VOLUME_LEVELS;

}


/****************************************************************************
 *
 * FUNCTION NAME = init_submit_cdb
 *
 * DESCRIPTION   = init time submit cdb routine
 *
 *                 VOID init_submit_cdb (NPUNITCB pUnitCB, NPIORB_CDB pIORB)
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 pIORB           - pointer to IORB
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID init_submit_cdb(pUnitCB, pIORB)

NPUNITCB pUnitCB;
NPIORB_CDB pIORB;
{

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

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


    /*
    ** Submit CDB.  Wait till done.
    */

    pIORB->apt.iorbh.NotifyAddress = &InitPost;

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

    while ( !(pIORB->apt.iorbh.Status & IORB_DONE) )
       DevHelp_ProcBlock( (ULONG) pIORB, (ULONG) 200, WAIT_IS_INTERRUPTABLE );

}



/****************************************************************************
 *
 * FUNCTION NAME = GetProductID
 *
 * DESCRIPTION   = get product id code
 *
 *                 VOID   GetProductID  (NPUNITCB pUnitCB)
 *
 * INPUT         = pUnitCB          - pointer to UnitCB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID GetProductID (pUnitCB)

NPUNITCB pUnitCB;
{

   USHORT n, rc, product_id_length;

   pUnitCB->DeviceInfo.vendor_id_code = 0;
   pUnitCB->DeviceInfo.product_id_code = 0;

   for ( n = 0; n < VENDOR_ID_COUNT; ++n)
   {
      rc = Strncmp(pUnitCB->InquiryData.vendor_id,
                   vendor_id_table[n].vendor_id_string,
                   8 );

      if ( rc )
      {
         pUnitCB->DeviceInfo.vendor_id_code =
                              vendor_id_table[n].vendor_id_code;
         break;
      }
   }


   product_id_length = 24;

   for ( n = 0; n < PRODUCT_ID_COUNT; ++n)
   {
      rc = Strncmp(pUnitCB->InquiryData.vendor_id,
                   product_id_table[n].product_id_string,
                   product_id_length );

      if ( rc )
      {
         /* if this is a NEC 260, is it ATAPI compliant? */
         /* mark the product_id_code if it is not        */

         if ( product_id_table[n].product_id_code == NEC_260_17B )
         {
            if (Strncmp(pUnitCB->InquiryData.revision_level,"1.01",4 ))
            {
               pUnitCB->DeviceInfo.product_id_code =
                                     product_id_table[n].product_id_code;
            }
         }
         else
         {
            pUnitCB->DeviceInfo.product_id_code =
                                 product_id_table[n].product_id_code;
         }
         break;
      }
   }

   /*
   ** If we find a SCSI-2 drive from a known vendor with a product id
   ** which is different that those known models listed in the table,
   ** then assume it is a future drive model which is upwardly compatible
   ** with it latest known drive, and assign the product id as the last
   ** known drive.  If the drive is truly upwards compatible, then it allows
   ** this level of the CD-ROM driver to work with future drives, and avoids
   ** the need to re-release the driver.
   */

   if ( (pUnitCB->DeviceInfo.product_id_code == 0) &&
        (pUnitCB->InquiryData.ansi_version == 2)  &&
        (pUnitCB->DeviceInfo.interface_type == INTERFACE_SCSI)  )
   {
      switch (pUnitCB->DeviceInfo.vendor_id_code)
      {
         case IBM:
         case TOSHIBA:
            pUnitCB->DeviceInfo.product_id_code = TOSHIBA_3401;
            break;

         case SONY:
            pUnitCB->DeviceInfo.product_id_code = SONY_561;
            break;

         case HITACHI:
            pUnitCB->DeviceInfo.product_id_code = HITACHI_6750;
            break;

         case TEXEL:
            pUnitCB->DeviceInfo.product_id_code = TEXEL_3024K;
            break;

         case PIONEER:
            pUnitCB->DeviceInfo.product_id_code = PIONEER_604X;
            break;

         case CHINON:
            pUnitCB->DeviceInfo.product_id_code = CHINON_535;
            break;

         case LMS:
            pUnitCB->DeviceInfo.product_id_code = LMS_215;
            break;

         case NEC:
            pUnitCB->DeviceInfo.product_id_code = NEC_84_1;
            break;
      }
   }
}


/****************************************************************************
 *
 * FUNCTION NAME = InitPost
 *
 * DESCRIPTION   = Dummy notification callout for ADDs during init processing
 *
 *                 VOID FAR InitPost(PIORB pIORB)
 *
 * INPUT         = pIORB           - pointer to IORB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID FAR InitPost(pIORB)

PIORB pIORB;
{

}


/****************************************************************************
 *
 * FUNCTION NAME = InitCBPool
 *
 * DESCRIPTION   = Initialize the control block pool
 *
 *                 Initializes the control block pool which is used for IORB,
 *                 CWA and FTDB allocation.
 *
 *                 USHORT InitCBPool ()
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID InitCBPool ()
{
   NPUNITCB pUnitCB;
   NPBYTE   pCB;
   NPIORBH  pIORB;
   USHORT   NumCBs, CBSize, i;
   USHORT   NumIORBs = 0;

   /*
   ** Calculate the size of the Pool
   */

   CBSize = sizeof(IORB_CDB);

   /*
   ** Calculate the number of IORBs to allocate by summing the
   ** Queuing Counts in each UnitCB.
   */
   NumIORBs = NumCDROMDrives * MAX_IORBS_PER_DRIVE;
   if (NumIORBs > MAX_IORBS_PER_POOL)
      NumIORBs = MAX_IORBS_PER_POOL;

   NumCBs = NumIORBs;

   FreePoolSpace = (USHORT) &InitData - (USHORT) pNextFreeCB;

   /*
   **  Make sure Pool Size isnt greater than available space left in
   **  the data segment.
   */
   if ( ((ULONG)NumCBs * CBSize) < (ULONG)FreePoolSpace)
      PoolSize = NumCBs * CBSize;
   else
   {
      NumCBs = FreePoolSpace / CBSize;
      PoolSize = NumCBs * CBSize;
   }

   /*
   ** Initialize the Control Block Pool
   */

   CB_FreeList = pNextFreeCB;

   pCB = CB_FreeList;

   for (i = 0; i < NumCBs; i++)
   {

      ((NPIORBH) pCB)->pNxtIORB = (PIORBH) (pCB + CBSize);
      pCB += CBSize;
   }

   ((NPIORBH) (pCB - CBSize))->pNxtIORB = 0;    /* Zero terminate list */

   pNextFreeCB = pCB;


   /*
   ** Allocate one dedicated IORB to each Unit
   */

   pCB = CB_FreeList;

   for (pUnitCB = UnitCB_Head, i = 0; i < NumUnitCBs; i++, pUnitCB++)
   {
      pUnitCB->pDedicatedIORB = (NPIORBH) pCB;
      ((NPIORBH) pCB)->pNxtIORB = 0;
      pCB += CBSize;
   }

   CB_FreeList = pCB;          /* Free List now starts past dedicated IORBs */


}


/****************************************************************************
 *
 * FUNCTION NAME = Init_Trace
 *
 * DESCRIPTION   = Initialize internal trace buffer
 *
 *                 VOID Init_Trace ()
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID Init_Trace()
{

   PPVOID   ppSysInfoSeg;
   NPUNITCB pUnitCB;
   USHORT   i;

   /*
   ** Get pointer to RAS Major Event Code Table
   */
/*
** DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &ppSysInfoSeg);
**
** SELECTOROF(pSysInfoSeg) = (USHORT) *ppSysInfoSeg;
** OFFSETOF(pSysInfoSeg) = 0;
**
** pSIS_mec_table = ((PInfoSegGDT)pSysInfoSeg)->SIS_mec_table;
*/
   /*
   ** Set up internal error tracing buffer.
   ** Note: Always will be enough since init data is > size of trace buffer
   */
   pCDTraceBuf  = pNextFreeCB;
   pNextFreeCB += TRACEBUF_SIZE;
   pCDTraceHead = pCDTraceBuf;
   pCDTraceEnd  = pCDTraceBuf + TRACEBUF_SIZE;

}



/****************************************************************************
 *
 * FUNCTION NAME = GetInitParms
 *
 * DESCRIPTION   = Get init parms from BASEDEV command line
 *
 *                 VOID GetInitParms (PRPINITIN pRP);
 *
 * INPUT         = pRP              - Pointer to init request packet
 *
 * OUTPUT        = VOID
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID GetInitParms (pRP)

PRPINITIN pRP;

{
   PSZ    pCmdString;
   PSZ    pCmdStringStart;
   USHORT i;
   UCHAR  CmdParm;
   BYTE   n;

   pCmdStringStart = pRP->InitArgs;

   if (pRP->rph.Cmd == CMDInitBase)
      OFFSETOF(pCmdStringStart)=((PDDD_PARM_LIST)pRP->InitArgs)->cmd_line_args;

   pCmdString = pCmdStringStart;

   /*
   ** Fold characters to upper case
   */
   for (i = 0; *pCmdString != 0 && i < 40; i++, pCmdString++)
   {
      if (*pCmdString >= 'a' &&  *pCmdString <= 'z')
         *pCmdString = *pCmdString - ('a' - 'A');
   }


   pCmdString = pCmdStringStart;

   for (i = 0; *pCmdString != 0 && i < 40; i++, pCmdString++)
   {
      if (*pCmdString == '/')
      {
         pCmdString++;
         switch (*pCmdString)
         {
            case 'Q':
                CDFlags |= CDF_QUIET;              /* Turn on quiet flag */
                break;

            case 'V':
                CDFlags |= CDF_VERBOSE;            /* Turn on verbose flag */
                break;

            case 'R':
                n = 0;
                pCmdString++;
                if ( *pCmdString == ':' )
                {
                   pCmdString++;
                   if ( (*pCmdString >= '0') && (*pCmdString <= '9') )
                   {
                      n = *pCmdString - '0';
                      if ((*(pCmdString+1) >= '0') && (*(pCmdString+1) <= '9'))
                      {
                         n = 10 * n + ( *(pCmdString+1) - '0');
                         pCmdString++;
                      }
                   }
                }

                if (n < 23)
                   ReserveDriveLetters = n;

                break;
         }
      }
   }
}

