/*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 = FLTINIT.C
 *
 * DESCRIPTIVE NAME = Common initialization routines for filters
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define INCL_NOBASEAPI
#define INCL_NOPMAPI

/* #define INCL_INITRP_ONLY */

#include <flthdr.h>
#include <devclass.h>


VOID   NEAR FLT_Build_UnitCBs (void);
VOID   NEAR BuildIORB_UnitControl (USHORT, NPUNITCB);
VOID   NEAR BuildCDB_Inquiry (NPUNITCB);
BOOL   NEAR CheckVendorID (NPUNITCB);
VOID   NEAR BuildIORB_PassthruCDB (NPUNITCB, ULONG, ULONG);
USHORT NEAR InitSubmitIORB_Wait (NPUNITCB);


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


USHORT    InitData=0;
UCHAR     InitTimeIORB[sizeof(IORB_CDB)]={0};
struct    Inquiry_Data inquiry_data = {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 */

/****************************************************************************
 *
 * FUNCTION NAME = CD_Filter_Init
 *
 * DESCRIPTION   = Initialization for filters
 *
 *
 * INPUT         = Pointer to Request Packet
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT near CD_Filter_Init(pRP)

PRPINITIN  pRP;
{
   USHORT     rc;
   PRPINITOUT pRPO = (PRPINITOUT) pRP;   /* Output for Init RP            */

/* _asm {int 3}; */

   /* Initialize various variables */

   FilterFlags |= FF_INIT_TIME;         /* Turn on init time flag         */

   Device_Help = pRP->DevHlpEP;         /* Save ptr to devhelp function   */

   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);




   /* Build the Unit Control blocks for the filtered CD-ROM drives */

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

   if (NumCDROMDrives == 0)             /* If no drives, then deinstall   */
   {
      pRPO->Unit = 0;
      pRPO->CodeEnd = 0;
      pRPO->DataEnd = 0;
      ZeroFSGS();
      rc = STDON + STERR + ERROR_I24_QUIET_INIT_FAIL;
   }
   else              /* At least 1 CD-ROM drive exists to filter requests for */
   {

     /* Return the end of the code and data segments */

      pRPO->Unit = 0;
      pRPO->CodeEnd = (USHORT) CD_Filter_Init;
      pRPO->DataEnd = (USHORT) &Filter_EndofData;
      rc = STDON;

  }
  /* Dont allow another INIT command to come in */

   FilterFlags &= ~FF_INIT_TIME;        /* Turn off init time flag        */
   InitComplete = 1;

   return(rc);                          /* Done with init, so return      */
}

/****************************************************************************
 *
 * FUNCTION NAME = FLT_Build_UnitCBs
 *
 * DESCRIPTION   = Build Unit Control Blocks ()
 *
 *                 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 filter driver manages.
 *
 *                 NOTES: UnitCBs built for each filtered CD-ROM target.
 *                        NumCDRomDrives = Number of UnitCBs built
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

void FLT_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;         /* Index pointers               */
  USHORT                FilterADDHandle2; /* Filter Handle               */
  USHORT                unit_handle;
  USHORT                NumDrivers;
  struct
  DevClassTableStruc far *pDriverTable;  /* ptr to registered ADD EPs    */
  VOID (FAR * DriverEP) ();              /* Driver entry point           */
  VOID (FAR * DriverEPF)();              /* Filter Driver entry point    */
  VOID (FAR * ADD_DriverEP) ();          /* Driver entry point           */

  /*
  ** 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 Filter CD-ROM type devices whose identify string
           ** is Toshiba and who's support level in SCSI-1.
           */

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

              unit_handle =  pAdapterInfo->UnitInfo[k].UnitHandle,

              pUnitCB = &UnitCBTable[NumCDROMDrives];
              pUnitCB->UnitInfo = pAdapterInfo->UnitInfo[k];
              pUnitCB->ADDUnitHandle = unit_handle;

              BuildIORB_UnitControl (IOCM_ALLOCATE_UNIT, pUnitCB);

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

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

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

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

              /*
              ** If allocation succeeded then issue inquiry command to see
              ** it it's a CD-ROM drive we need to filter.
              */

              if ( !(pIORB->iorbh.Status & IORB_ERROR) )
              {
                 /*
                 ** Save the callable entry point of the adapter driver. If
                 ** the unit is being filtered, use the entry point of the
                 ** filter driver.
                 */

                 if (FilterADDHandle2 == 0)
                     pUnitCB->AdapterDriverEP = DriverEP;
                 else
                 {
                    OFFSETOF(pUnitCB->AdapterDriverEP) = OFFSETOF(DriverEPF);
                    SELECTOROF(pUnitCB->AdapterDriverEP)=SELECTOROF(DriverEPF);
                 }


                 if ( CheckVendorID(pUnitCB) )
                 {

                    /* Register filter if not already registered */

                    if ( !(FilterFlags & FF_REGISTERED) )
                    {
                       DevHelp_RegisterDeviceClass(
                                         (NPSZ)    FILTERNAME,
                                         (PFN)     Filter_IORB,
                                         (USHORT)  FILTERFLAGS,
                                         (USHORT)  DRIVERCLASS_ADD,
                                         (PUSHORT) &FilterADDHandle    );


                       FilterFlags |= FF_REGISTERED;
                    };

                    /* Issue change unit info to update FilterADDHandle */

                    pUnitCB->UnitInfo.FilterADDHandle = FilterADDHandle;
                    pUnitCB->UnitInfo.UnitHandle = NumCDROMDrives;

                    BuildIORB_UnitControl (IOCM_CHANGE_UNITINFO, pUnitCB);

                    rc = InitSubmitIORB_Wait (pUnitCB);

                    NumCDROMDrives++;
                 }

                 /* Deallocate the unit  */

                 BuildIORB_UnitControl (IOCM_DEALLOCATE_UNIT, pUnitCB);

                 rc = InitSubmitIORB_Wait (pUnitCB);
              }
           }
        }  /* end unit loop */
     }  /* end adapter loop */
  }  /* end driver loop */
}


/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_UnitControl
 *
 * DESCRIPTION   = This routine build a unit control IORB.
 *
 * INPUT         = command_modifier   - IORB command modifier code
 *                 pUnitCB            - unit info pointer
 *                                      (valid only for CHANGE_UNIT_INFO)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID BuildIORB_UnitControl (command_modifier, pUnitCB)

USHORT     command_modifier;
NPUNITCB   pUnitCB;

{
   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 = pUnitCB->ADDUnitHandle;
   pIORB->iorbh.RequestControl = IORB_ASYNC_POST;
   pIORB->iorbh.NotifyAddress = &InitPost;
   pIORB->Flags = 0;

   if ( (command_modifier == IOCM_ALLOCATE_UNIT ||
         command_modifier == IOCM_DEALLOCATE_UNIT) )
   {
      pIORB->pUnitInfo = 0;
      pIORB->UnitInfoLen = 0;
   }
   else
   {
      pIORB->pUnitInfo = (PUNITINFO) &pUnitCB->UnitInfo;
      pIORB->UnitInfoLen = sizeof(UNITINFO);
   }
}

/****************************************************************************
 *
 * FUNCTION NAME = CheckVendorID
 *
 * DESCRIPTION   = check vendor id string
 *
 * INPUT         = pUnitCB          - pointer to UnitCB
 *
 * OUTPUT        = USHORT           - vendor match flag
 *                                    = 0, no match
 *                                    = 1, string matches
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL CheckVendorID (pUnitCB)

NPUNITCB pUnitCB;
{

   NPIORB_CDB pIORB;
   USHORT     n, rc;

   BOOL match = NO;

   /* Get the inquiry data and check for matching id string */

   BuildCDB_Inquiry(pUnitCB);

   rc = InitSubmitIORB_Wait (pUnitCB);

   for ( n = 0; n < vendor_count; ++n)
   {
      rc = Strncmp (inquiry_data.vendor_id, Vendor[n].name, 8 );

      if ( rc )
      {
         /* Always load filter for SCSI-1 device.               */
         /* Or, if Chinon 535 with Q level firmware.            */

         if ((Strncmp(inquiry_data.vendor_id,"CHINON  CD-ROM CDS-535  Q",25)) ||
              (inquiry_data.ansi_version < 2) )
         {
             match = YES;
             for (n = 0; n < 16; n++)
                pUnitCB->product_id[n] = inquiry_data.product_id[n];
             break;
         }
      }
   }

   return(match);
}

/****************************************************************************
 *
 * FUNCTION NAME = BuildIORB_PassthruCDB
 *
 * DESCRIPTION   = This routine sets up a CDB passthru IORB
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *                 data_length     - length of data to transfer
 *                 ppData          - physical address of data buffer
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID BuildIORB_PassthruCDB (pUnitCB, data_length, ppData)

NPUNITCB        pUnitCB;
ULONG           data_length;
ULONG           ppData;

{
   NPIORB_CDB    pIORB;
   NPIORB_DMWORK pDMWork;

   pIORB = (NPIORB_CDB) InitTimeIORB;

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

   *pIORB = default_iorb_cdb;

    pIORB->apt.iorbh.UnitHandle = pUnitCB->ADDUnitHandle;
    pIORB->apt.iorbh.NotifyAddress = &InitPost;

   /* If data transfer involved, setup the scatter/gather list     */

   if (data_length != 0)
   {
      pIORB->apt.cSGList = 1;
      pIORB->apt.pSGList = (PVOID) pIORB;
      OFFSETOF(pIORB->apt.pSGList) = (USHORT) ( &(pDMWork->SGList) );
      pIORB->apt.ppSGLIST =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pDMWork->SGList)) );

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

      pDMWork->SGList.XferBufLen = data_length;
   }

   /* Fill in the pointer to the SCSI CDB */

   pIORB->apt.pControllerCmd = (PVOID) pIORB;
   OFFSETOF(pIORB->apt.pControllerCmd) = (USHORT) ( &(pIORB->CDB) );
   pIORB->apt.ppSCB =
          (ULONG) (ppDataSeg + (ULONG) ((USHORT) &(pIORB->CDB)) );


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

   (USHORT) pIORB->apt.iorbh.pStatusBlock = (USHORT) ( &(pIORB->status_block) );

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

}

/****************************************************************************
 *
 * FUNCTION NAME = BuildCDB_Inquiry
 *
 * DESCRIPTION   = This routine builds a SCSI Inquiry CDB.
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID BuildCDB_Inquiry(pUnitCB)

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

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

  pIORB = (NPIORB_CDB) InitTimeIORB;

  /* Build a CDB passthru IORB */

  BuildIORB_PassthruCDB (pUnitCB,
               sizeof(struct Inquiry_Data),
               (ULONG) (ppDataSeg+(ULONG)((USHORT)&inquiry_data)));

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

  /* Setup the CDB */

  pCDB = (struct CDB_Inquiry NEAR *) &pIORB->CDB;
  pCDB->OpCode = SCSI_INQUIRY;
  pCDB->LUN = pUnitCB->UnitInfo.UnitSCSILUN;
  pCDB->alloc_length = sizeof(struct Inquiry_Data);

}

/****************************************************************************
 *
 * FUNCTION NAME = InitSubmitIORB_Wait
 *
 * DESCRIPTION   = This routine submits a request to an IORB.
 *
 * INPUT         = pUnitCB         - pointer to UnitCB
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT InitSubmitIORB_Wait (pUnitCB)

NPUNITCB pUnitCB;
{
   NPIORB pIORB;

   pIORB = (NPIORB) InitTimeIORB;

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

   while ( !(pIORB->Status & IORB_DONE) )
   ;

}

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

VOID FAR InitPost(pIORB)

PIORB pIORB;
{

}

/****************************************************************************
 *
 * FUNCTION NAME = Strncmp
 *
 * DESCRIPTION   = string compare
 *                 This is like the standard C function, except it is boolean
 *
 * INPUT         = string1         - string 1
 *                 string2         - string 2
 *                 length          - length of string
 *
 * OUTPUT        = BOOLEAN, 1 if equal, 0 if not equal
 *
 * RETURN-NORMAL =
 *
 * RETURN-ERROR  =
 *
 ****************************************************************************/

Strncmp ( string1, string2, length )

UCHAR   *string1, *string2;
USHORT  length;

{
    BOOL    equal = TRUE;

    while ( length-- > 0 )
    {
        if ( *string1++ != *string2++ )
        {
            equal = FALSE;
            break;
        }
    }

    return ( equal );
}

