/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "@(#)testcfg.c  6.3 92/02/13";*/

/*** TESTCFG.C - TESTCFG$ Device Driver
 *
 *   DESCRIPTION
 *
 *     TESTCFG is a device driver used for installation of OEM
 *     device drivers.  Using TESTCFG services, it will be possible
 *     to determine hardware adapter presence.
 *     TESTCFG provides DosDevIOCtl services to ring 3 code.  These
 *     services are:
 *
 *        o  Determine Bus Type
 *        o  Determine POS IDs
 *        o  Determine EISA IDs
 *        o  Access Adapter Memory (make a copy for app to read)
 *        o  Input/Output data through IO ports
 *
 *   MODIFICATION HISTORY
 *
 * 6.1  01/15/92 CP20          J.Muir    Original Version
 * 6.2  01/22/92 CP20          J.Muir    Removed print banner
 * 6.3  02/12/92 CP20 B733010  K.Harris  Query ABIOS for RB length that
 *                                         should be used for Read POS data.
 * 1.4  12/02/92 r206 p057353  J.Muir     Use MCA flag from DosQueryABIOSSupport
 * 1.x  09/17/94 rbdd          J.Grimm    Resource Manager
 *      03/30/95               C.Coy/E.Coy Integrate IBM 486SLC fix
 *      05/08/96               F.Schroeder Added IEEE-1284 Query Device ID
 */

#include "testcfg.h"
#include "IORB.h"
#include "SCSI.h"
#include "devclass.h"
#include "tcfgscsi.h"
#include "tcfgx.h"
#include "rmcalls.h"
#include "tcfgex.h"

/*** main - Strategy entry for all TESTCFG$ requests
 *
 *   Main router for TESTCFG$ device driver request packets.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     Based on request packet command, call either Init or Ioctl routines
 */
USHORT main(PREQPACKET rp)
{
 USHORT reqrc;                                 /* Request completion code */
 USHORT i;                                     /* loop control variable */

  switch(rp->RPcommand) {                      /* Request packet command  */

    case RPINIT :                              /* Init */
        Device_Help = rp->s.Init.DevHlp;

        for (i = 0; i < 3; i++)                /* IEEE-1284 support */
          adapterInit( (PSZ)rp );              /* initialize adapter instance */

        reqrc = CFG_Init(rp);
        APMPresent = IsAPMPresent();
        break;

    case RPDEINSTALL:                          /* De Install */
    case RPOPEN:                               /* Open       */
    case RPCLOSE:                              /* Close      */
        reqrc = RPDONE;
        break;

    case RPIOCTL:                              /* IOCTL */
        if ( (rp->s.IOCtl.category == 0x05) && /* IEEE-1284 support */
             (rp->s.IOCtl.function == 0x74) )  /* Query Device ID */
        {
          reqrc = parDevIDQuery( (PSZ)rp );
          break;
        }

        if(rp->s.IOCtl.category == CFG_CATEGORY)
          reqrc = CFG_IOCtl(rp);
        else
          reqrc = RPDONE | RPERR | ERROR_INVALID_PARAMETER;
        break;

    case RPINIT_COMPLETE:                      /* Init Complete */
        reqrc = parInitComp( (PSZ)rp );        /* IEEE-1284 support */
        break;

    default:
        reqrc = (RPDONE | RPERR | ERROR_BAD_COMMAND);
    }

  return(reqrc);
}

/*** CFG_IOCtl - Router for all TESTCFG$ IOCTL requests
 *
 *   When a program issues a DosDevIOCtl command to TESTCFG, it will
 *   end up in this routine.  CFG_IOCtl decides which action to perform
 *   base on the category/function selected by the caller.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     Based on IOCtl function, call one of the below:
 *       CFG_Read_POSID
 *       CFG_Read_EISAID
 *       CFG_Read_ADPMEM
 *       CFG_In_IO
 *       CFG_Out_IO
 *       CFG_Misc_Query
 *       CFG_SCSI_Inquiry
 *
 */
USHORT far CFG_IOCtl(PREQPACKET rp)
{
 USHORT ioctlrc;

 switch(rp->s.IOCtl.function) {

     case CFG_READ_POSID:
          ioctlrc = CFG_Read_POSID(rp);
          break;

     case CFG_READ_EISAID:
          ioctlrc = CFG_Read_EISAID(rp);
          break;

     case CFG_READ_ADPMEM:
          ioctlrc = CFG_Read_ADPMEM(rp);
          break;

     case CFG_IN_IO:
          ioctlrc = CFG_In_IO(rp);
          break;

     case CFG_OUT_IO:
          ioctlrc = CFG_Out_IO(rp);
          break;

     case CFG_MISC_QUERY:
          ioctlrc = CFG_Misc_Query(rp);
          break;

     case CFG_SCSI_INQUIRY:
          ioctlrc = CFG_SCSI_Inquiry(rp);
          break;

     default:
          ioctlrc = RPDONE | RPERR | ERROR_INVALID_PARAMETER;
          break;
     }
 return(ioctlrc);
 }

/*** CFG_Read_POSID - Read Programmable Option Select IDs
 *
 *   If we are running on a MicroChannel machine, we would like
 *   to determine the POS IDs for all of the adapters.  Using
 *   ABIOS is how the information is determined.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     if ABIOS present
 *       Verify CMD field
 *       if CMD is zero
 *         Verify POS ID Data Structure
 *         Copy POS ID information from TESTCFG to user
 *       else
 *         Return invalid parameter
 *     else
 *       Return invalid parameter
 */
USHORT CFG_Read_POSID(PREQPACKET rp)
{
 PADPPOS pPOS;                         /* Pointer to POS ID data array */
 USHORT i;                             /* index variable               */
 PCMD   pCmd;                          /* Pointer to IOCtl Cmd         */

  if(fMCAPresent) {

    /* Get and Verify CMD Field */
    pCmd = (PCMD)rp->s.IOCtl.parameters;
    if(VerifyAccess(pCmd,sizeof(ULONG),VERIFY_READACCESS))
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

    if(*pCmd==0) {

      /* Get and Verify POS struct */
      pPOS = (PADPPOS)rp->s.IOCtl.buffer;
      if(VerifyAccess(pPOS,sizeof(ADPPOS),VERIFY_READWRITEACCESS))
        return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

      /* copy information to user POS structure */
      for(i=0;i<=CFG_MAX_POS_SLOTS;i++)
        pPOS->POSID[i]= POSCardID[i];
      }
    else
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  return(RPDONE);
}

/*** CFG_Read_EISAID - Read EISA IDs
 *
 *   If we are running on a EISA machine, we would like
 *   to determine the EISA IDs for all of the adapters.  Using
 *   OEMHLP is how the information is determined.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     if EISA present
 *       Verify CMD field
 *       if CMD is zero
 *         Verify EISA ID Data Structure
 *         Copy EISA ID information from TESTCFG to user
 *       else
 *         Return invalid parameter
 *     else
 *       Return invalid parameter
 */
USHORT CFG_Read_EISAID(PREQPACKET rp)
{
 PADPEISA pEISAInfo;
 PCMD   pCmd;
 USHORT i;

  if(fEisaPresent) {

    /* Get and Verify CMD Field */
    pCmd = (PCMD)rp->s.IOCtl.parameters;
    if(VerifyAccess(pCmd,sizeof(ULONG),VERIFY_READACCESS))
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

    if(*pCmd==0) {

      /* Get and Verify EISA ID Data Area */
      pEISAInfo = (PADPEISA)rp->s.IOCtl.buffer;
      if(VerifyAccess(pEISAInfo,sizeof(ADPEISA),VERIFY_READWRITEACCESS))
        return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

      /* Copy the EISA ID information from TESTCFG to user space */
      for(i=0;i<CFG_MAX_EISA_SLOTS;i++)
        pEISAInfo->EISAID[i] = EisaCardID[i];

      }
    else
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  return(RPDONE);
}

/*** CFG_Read_ADPMEM - Copy areas between 0xC0000 and 0xFFFFF to user space
 *
 *   This routine is used to get copies of ROM/RAM locations
 *   for adapter and system microcode.  This access is Read Only.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     Verify CMD field
 *     if CMD is zero
 *       Verify Memory structure
 *       if Memory Block is in range between 0xC0000 to 0xFFFFF
 *         Verify User buffer
 *         Get virtual address to selected range
 *         Copy the memory
 *         Release virtual address
 *       else
 *         Return invalid parameter
 *     else
 *       Return invalid parameter
 */
USHORT CFG_Read_ADPMEM(PREQPACKET rp)
{
 PCMD   pCmd;
 PBYTE  pAdp;
 PADPMEM pMem;
 PBYTE pBuffer;
 USHORT i;

  /* Get and Verify CMD Field */
  pCmd = (PCMD)rp->s.IOCtl.parameters;
  if(VerifyAccess(pCmd,sizeof(ULONG),VERIFY_READACCESS))
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  if(*pCmd==0) {

    /* Get and Verify memory structure */
    pMem = (PADPMEM)rp->s.IOCtl.parameters;
    if(VerifyAccess(pMem,sizeof(ADPMEM),VERIFY_READACCESS))
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

    /* If memory range is valid */
    if((pMem->addr0 >= CFG_ADP_MIN_ADDR)&&
       (pMem->addr0 <= CFG_ADP_MAX_ADDR)&&
       ((pMem->addr0 + pMem->length0)>= CFG_ADP_MIN_ADDR)&&
       ((pMem->addr0 + pMem->length0)<= CFG_ADP_MAX_ADDR)) {

      /* Get and Verify user buffer */
      pBuffer = (PBYTE)rp->s.IOCtl.buffer;
      if(VerifyAccess(pBuffer,pMem->length0,VERIFY_READWRITEACCESS))
        return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

      /* get a virtual address to physical memory */
      PhysToVirt(pMem->addr0,pMem->length0,(FARPOINTER)&pAdp);

      /* Copy the information to the users space */
      for(i=0;i<(pMem->length0);i++)
        *pBuffer++ = *pAdp++;

      /* release the address */
      UnPhysToVirt();
      }
    else
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  return(RPDONE);
}

/*** CFG_In_IO - Input values from ports
 *
 *   This routine is used to input from IO ports.  Since applications
 *   are ring 3, they can't do I/O. With this feature, they can let
 *   TESTCFG do the work at ring 0. System ports are not allowed. Any
 *   port below 0x100 is denied.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     Verify IO packet struct
 *     Verify IO data struct
 *     if Port value is 0x100 or higher
 *         Based on data size, input a byte, word, or dword
 *     else
 *       Return invalid parameter
 */
USHORT CFG_In_IO(PREQPACKET rp)
{
 PIOPKT  pIOReq;
 PIODATAPKT pIOData;
 HRESOURCE  ResourceHandle;
 RESOURCESTRUCT Resource;


  /* Get and Verify the IO packet parameters */
  pIOReq = (PIOPKT)rp->s.IOCtl.parameters;
  if(VerifyAccess(pIOReq,sizeof(IOPKT),VERIFY_READACCESS))
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  /* Get and Verify the IO data parameters */
  pIOData = (PIODATAPKT)rp->s.IOCtl.buffer;
  if(VerifyAccess(pIOData,sizeof(IODATAPKT),VERIFY_READWRITEACCESS))
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  /* If port non-system port, then allow IO */
  if(pIOReq->io_address>=CFG_IO_MIN_PORT) {
    Resource.ResourceType              = RS_TYPE_IO;
    Resource.IOResource.BaseIOPort     = pIOReq->io_address;
    Resource.IOResource.NumIOPorts     = pIOReq->data_width;
    Resource.IOResource.IOFlags        = RS_IO_EXCLUSIVE;
    Resource.IOResource.IOAddressLines = 16;

    if ( RMAllocResource( DriverHandle, &ResourceHandle, &Resource ) )
    {
       return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    }

    switch(pIOReq->data_width) {

      case CFG_IO_BYTE:
        pIOData->d.ucData = InB(pIOReq->io_address);
        break;

      case CFG_IO_WORD:
        pIOData->d.usData = InW(pIOReq->io_address);
        break;

      case CFG_IO_DWORD:
        pIOData->d.ulData = InDW(pIOReq->io_address);
        break;

      default:
        return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
        break;
      }

      RMDeallocResource( DriverHandle, ResourceHandle);

  }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);



 return(RPDONE);
}

/*** CFG_Out_IO - Input values from ports
 *
 *   This routine is used to output to IO ports.  Since applications
 *   are ring 3, they can't do I/O. With this feature, they can let
 *   TESTCFG do the work at ring 0. System ports are not allowed. Any
 *   port below 0x100 is denied.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     Verify IO packet struct
 *     if Port value is 0x100 or higher
 *         Based on data size, output a byte, word, or dword
 *     else
 *       Return invalid parameter
 */
USHORT CFG_Out_IO(PREQPACKET rp)
{
 PIOPKT  pIOReq;
 HRESOURCE  ResourceHandle;
 RESOURCESTRUCT Resource;

  /* Get and verify IO packet parameters */
  pIOReq = (PIOPKT)rp->s.IOCtl.parameters;
  if(VerifyAccess(pIOReq,sizeof(IOPKT),VERIFY_READACCESS))
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  /* If port is non-system, then allow IO */
  if(pIOReq->io_address>=CFG_IO_MIN_PORT) {
    Resource.ResourceType              = RS_TYPE_IO;
    Resource.IOResource.BaseIOPort     = pIOReq->io_address;
    Resource.IOResource.NumIOPorts     = pIOReq->data_width;
    Resource.IOResource.IOFlags        = RS_IO_EXCLUSIVE;
    Resource.IOResource.IOAddressLines = 16;

    if ( RMAllocResource( DriverHandle, &ResourceHandle, &Resource ) )
    {
       return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    }

    switch(pIOReq->data_width) {

      case CFG_IO_BYTE:
        OutB(pIOReq->io_address,pIOReq->d.ucData);
        break;

      case CFG_IO_WORD:
        OutW(pIOReq->io_address,pIOReq->d.usData);
        break;

      case CFG_IO_DWORD:
        OutDW(pIOReq->io_address,pIOReq->d.ulData);
        break;

      default:
        return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
        break;
      }

      RMDeallocResource( DriverHandle, ResourceHandle);
    }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  return(RPDONE);
}

/*** CFG_Misc_Query - Input values from ports
 *
 *   This routine is used for miscellaneous query functions.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     Verify CMD field
 *     if CMD is zero
 *       Get and Verify BusType Field
 *       Store BusType in user space
 *     else if CMD is one
 *       Get and Verify APM_Present field.
 *       Store APM_present in user space.
 *     else
 *       Return invalid parameter
 */
USHORT CFG_Misc_Query(PREQPACKET rp)
{
 PCMD   pCmd;
 PULONG pBusType;
 PULONG pAPMPresent;

  /* Get and Verify CMD Field */
  pCmd = (PCMD)rp->s.IOCtl.parameters;
  if(VerifyAccess(pCmd,sizeof(ULONG),VERIFY_READACCESS))
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  if(*pCmd==0) {

    /* Get and Verify BusType Field */
    pBusType = (PULONG)rp->s.IOCtl.buffer;
    if(VerifyAccess(pBusType,sizeof(ULONG),VERIFY_READWRITEACCESS))
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

    /* store BusType in user space */
    if(fEisaPresent)
      *pBusType = CFG_EISA_BUS;
    else if(fMCAPresent)
      *pBusType = CFG_MCA_BUS;
    else
      *pBusType = CFG_ISA_BUS;
    }
  else if (*pCmd == 1) {
    /* Get and Verify APMPresent Field */
    pAPMPresent = (PULONG)rp->s.IOCtl.buffer;
    if(VerifyAccess(pAPMPresent,sizeof(ULONG),VERIFY_READWRITEACCESS))
      return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    *pAPMPresent = APMPresent;
    }
  else
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);

  return(RPDONE);
}

/* CFG_Init After init time, everything below here is discarded.
 */

/*** CFG_Init - Init routine for TESTCFG
 *
 *   This routine initializes the TESTCFG device driver. Information is
 *   collected at this point and save in the data area.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      returns Device Driver Completion Code
 *
 *   PseudoCode
 *
 *     open the OEMHLP$ device driver
 *     if open was successful
 *       issue IOCTL to OEMHLP to determine BIOS information
 *       if not Microchannel machine
 *         Initialize EISA info
 *       close OEMHLP$ driver
 *       if MicroChannel machine
 *         Initialize POS info
 *       See if Advance Power Management BIOS is present
 *       write TESTCFG installation successful to screen
 *       adjust sizes of code and data segments to delete init stuff
 *     else
 *       write TESTCFG did not install to screen
 *       adjust sizes of code and data segs to zero
 *       return invalid parameter
 */
USHORT far CFG_Init(PREQPACKET rp)
{
  USHORT rc,action;
  USHORT hOEM;

  /* Set the Machine Specific Registers as required on the IBM 486SLC. */
  SlcWarp();

  /* open a link to the OEMHlp device driver */
  rc = DosOpen(OEMHelp,&hOEM,&action,0L,0,1,0x40,0L);

  /* If we were able to open OEMHLP, we can install TESTCFG, otherwise
   * it isn't worth trying.
   */
  if(rc==0) {
    /* Retrieve Model, Submodel, Revision and ABIOS_Present from OEMHLP$
     */
    DosDevIOCtl2(&BIOSInfo,                     /* BIOS Level Info Struct   */
                 sizeof(BIOSINFO),              /* Length of BIOS Struct    */
                 NULL,                          /* No parameters            */
                 NULL,                          /* No param length          */
                 OEM_BIOS_INFO,                 /* OEM BIOS_INFO call       */
                 OEM_CATEGORY,                  /* OEM Category 0x80        */
                 hOEM);                         /* Handle to OEM device drvr*/

    /* 1.4 r206 Use MCA flag instead of ABIOS flag */
    fMCAPresent = (FLAG)(DosQueryABIOSSupport(0L) & HW_CFG_MCA);

    /* If we have a non-MicroChannel machine, we need to separate
     * between EISA and ISA machines.  First we try to read the
     * EISA Slot Information for the planar.  If OEMHLP$ doesn't
     * allow this DosDevIOCtl, then we have an ISA machine.
     */
    if(!fMCAPresent)
      CFG_Init_EISA(hOEM);

    DosClose(hOEM);                               /* Close handle to OEMHLP$ */

    /* If MicroChannel machine, get MicroChannel POS Information
     * from ABIOS calls.
     */
    if(fMCAPresent)
      CFG_Init_POS();

    /* Set up pointer to data segment */

    pDataSeg = (PPOINTER) &pDataSeg;
    OFFSETOF(pDataSeg) = 0;

    /* Save away the physical address of our data segment */

    VirtToPhys((ULONG)pDataSeg, (PULONG) &ppDataSeg);


// 6.2  /*  Write installation successful message on the screen. */
//  DosPutMessage(1, strlen(InitMessage), InitMessage);

    RMCreateDriver(&DriverStruct, &DriverHandle);


    /* Adjust the Data and Code Segments to reflect size after Init */
    rp->s.InitExit.finalCS = (USHORT) &CFG_Init;
    rp->s.InitExit.finalDS = (USHORT) &InitData;
//  SegLimit(HIUSHORT((void far *) &Device_Help), &rp->s.InitExit.finalDS);

    return(RPDONE);
    }
  else {

    /* If we can't open OEMHLP$, we will de install */
    DosPutMessage(1, strlen(FailMessage), FailMessage);
    rp->s.InitExit.finalCS = 0;
    rp->s.InitExit.finalDS = 0;
    return(RPDONE | RPERR | ERROR_INVALID_PARAMETER);
    }
}

/*** CFG_Init_POS - Init POS ID data structure
 *
 *   This routine initializes the POS ID data.  Data is retrieved from
 *   ABIOS calls.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      NONE
 *
 *   PseudoCode
 *
 *     Get LID Entry for ABIOS POS
 *     Return if not successful.
 *     Get Required Length of Request Block.
 *     if successful and if sufficent storage
 *       for each slot
 *         get POS ID from ABIOS
 *         if ABIOS call successful
 *           assign POS ID
 *         else
 *           POS ID equals zero
 *     Free LID Entry
 */
void CFG_Init_POS(void)
{
 USHORT i,rc;             /* index and return code    */
 USHORT LID;              /* Logical ID               */
 ULONG  POSData;          /* POS data value (102-105) */

  /* Get LID Entry for POS */
  rc = GetLIDEntry(POS,0,1,&LID);
  if (rc) return ;

  /* Get length of RB to use for reading POS data. */
  POSLenRB.rb.RBLen = sizeof(POSLENRB);
  POSLenRB.rb.Func  = 0x01;
  POSLenRB.rb.LID   = LID;
  POSLenRB.rb.Unit  = 0;
  POSLenRB.rb.Resv1 = 0;
  POSLenRB.rb.Resv2 = 0;
  POSLenRB.Rsv1     = 0;
  POSLenRB.Rsv2     = 0;
  POSLenRB.Rsv3     = 0;
  rc = ABIOSCall( LID, &POSLenRB, 0);

  if ((rc==0) && (sizeof(POSRB) >= POSLenRB.RBLen)) {
    /* Initialize request block for reading POS data. */
    RB.rb.RBLen = POSLenRB.RBLen;       /* request block length        */
    RB.rb.Func  = 0x0b;                 /* read stored POS data to mem */
    RB.rb.LID   = LID;                  /* Logical ID                  */
    RB.rb.Unit  = 0;
    RB.DataBuf  = (ULONG)(FARPOINTER)&POSData;

    /* for each slot, get POS ID */
    for(i=0;i<=CFG_MAX_POS_SLOTS;i++) {
      RB.Slot = (UCHAR)i;
      rc = ABIOSCall(LID,&RB,0);
      if((rc==0)&&(RB.rb.RetCode==0))
        POSCardID[i] = RB.AdapterID;
      else
        POSCardID[i] = 0;
      }
    }

  /* Release LID Entry */
  FreeLIDEntry(LID);
  return;
}

/*** CFG_Init_EISA - Init EISA ID data structure
 *
 *   This routine initializes the EISA ID data.  Data is retrieved from
 *   OEMHLP IOCtl calls.
 *
 *   ENTRY
 *      PREQPACKET rp - pointer to Device driver request packet
 *
 *   EXIT
 *      NONE
 *
 *   PseudoCode
 *
 *     get EISA ID for slot 0 (planar)
 *     if Get EISA ID successful
 *       mark as EISA Machine
 *       save info for EISA ID slot 0
 *       for each remaining slot
 *         get EISA ID for slot
 *         if successful
 *           assign EISA ID
 *         else
 *           set EISA ID to zero
 *     else
 *       mark as ISA Machine
 */
void CFG_Init_EISA(USHORT hOEM)
{
 EISASLOTDATA EisaSlot;
 EISAPARM  EisaParm;
 USHORT i,rc;

  /* initialize EISA parameters */
  EisaParm.SubFunc = OEM_GET_SLOT_INFO;       /* EISA Get Slot Info     */
  EisaParm.Slot    = 0;                       /* Slot 0                 */

  /* Get EISA ID for slot 0 (planar) */
  rc = DosDevIOCtl2(&EisaSlot,                /* EISA Slot Information  */
                    sizeof(EISASLOTDATA),     /* Size of EISA Slot Info */
                    &EisaParm,                /* EISA Cmd parameters    */
                    sizeof(EISAPARM),         /* Size of EISA Cmd Parms */
                    OEM_GET_EISA_CONFIG,      /* IOCTL GET_EISA_CONFIG  */
                    OEM_CATEGORY,             /* Category 0x80          */
                    hOEM);                    /* handle to OEMHLP$      */

  /* If we are working with a true EISA machine, then continue to
   * read the IDs of the remaining adapters and store in data array.
   */
  if(rc==0) {
    fEisaPresent = TRUE;                      /* EISA marked as present */
    EisaCardID[0] = EisaSlot.CardID;          /* Copy Planar ID         */
    for(i=1;i<CFG_MAX_EISA_SLOTS;i++) {       /* For each possible slot */
      EisaParm.Slot    = (UCHAR) i;           /* Slot Number            */
      EisaSlot.CardID  = 0;                   /* Reset Adapter ID value */
      rc = DosDevIOCtl2(&EisaSlot,            /* EISA Slot Information  */
                        sizeof(EISASLOTDATA), /* Size of EISA Slot Info */
                        &EisaParm,            /* EISA Cmd parameters    */
                        sizeof(EISAPARM),     /* Size of EISA Cmd Parms */
                        OEM_GET_EISA_CONFIG,  /* IOCTL GET_EISA_CONFIG  */
                        OEM_CATEGORY,         /* Category 0x80          */
                        hOEM);                /* handle to OEMHLP$      */

      /* If IOCTL successful and EISA has adapter, then store away
         the adapter ID, otherwise mark as empty with a zero.
       */
      if((rc==0)&&(EisaSlot.Error==0))
        EisaCardID[i] = EisaSlot.CardID;
      else
        EisaCardID[i] = 0;
      }
    }
  else
    fEisaPresent = FALSE;                     /* Mark EISA as not present*/

  return;
}
