/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1996                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PARINIT.C                                             */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver initialization            */
/*                      routines.                                             */
/*                                                                            */
/*   FUNCTION: These routines handle the presence check for the parallel      */
/*             ports.                                                         */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             parInit                                                        */
/*             parDetermineHardware                                           */
/*             DetectECP                                                      */
/*             DetectIRQ                                                      */
/*             DefaultIRQ                                                     */
/*             IRQ7ISR                                                        */
/*             IRQ5ISR                                                        */
/*             PARTimeout                                                     */
/*             PARAllocPort                                                   */
/*             PARAllocIRQ                                                    */
/*             PARCreateAdapter                                               */
/*             PARCreateDevice                                                */
/*             getModel                                                       */
/*             stringncmp                                                     */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          96/03/01  Frank Schroeder                                         */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "par.h"

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  parInit                                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  Initializes the parallel port device driver.    */
/*                                                                    */
/* FUNCTION:  The function of this routine is to initialize the       */
/*            parallel port device driver.                            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  parInit                                              */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pInst-> adapter instance                                   */
/*         pRP-> kernel request packet                                */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  pRPO->CodeEnd = end of code segment                      */
/*           pRPO->DataEnd = end of data segment                      */
/*           pRP->Status = STATUS_DONE                                */
/*           pRP->Status = STDON + STERR + ERROR_I24_GEN_FAILURE      */
/*                                                                    */
/* INTERNAL REFERENCES:  AllocateInstance                             */
/*                       parDetermineHardware                         */
/*                       DestroyInstance                              */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMCreateDriver                               */
/*                       DevHelp_AllocGDTSelector                     */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void parInit( parInstance_t *pInst, RPH FAR *pRP)
{
  PRPINITOUT      pRPO;          /* output request packet far pointer */
  PRPINITIN       pRPI;          /* input request packet far pointer */
  PMACHINE_CONFIG_INFO  pMInfo;  /* ptr to machine config information pkt  */
  USHORT          BusType;       /* type of bus 1=ISA, 2=EISA, 4=MC */

  pRPI = (PRPINITIN) pRP;
  pRPO = (PRPINITOUT) pRP;

//  _asm { int 3 }

  if (FirstInit)
  {
     fInitTime = TRUE;
     FirstInit = FALSE;

     Device_Help = ((PRPINITIN)pRP)->DevHlpEP;  /* save devhlp entry point */

     /*---------------------------------------------------*/
     /* Register device driver virtual IDC entry point.   */
     /*---------------------------------------------------*/
     if ( DevHelp_RegisterPDD((NPSZ) plptname,
                              (PFN)  parLPTCMD_Entry) != 0 )
       goto parInit_Failed;                     /* then fail initialization */

     /*---------------------------------------------------*/
     /* Register device driver with resource manager      */
     /*---------------------------------------------------*/
     RMCreateDriver( &DriverStruct, &hDriver);
  }
  else
  {
    /*-----------------------------------------*/
    /* Allocate a parallel instance structure  */
    /*-----------------------------------------*/
    if ( !(pInst = AllocateInstance()) )
      goto parInit_Failed;                /* then fail initialization */

    /*---------------------------------------------------*/
    /* Initialize LPTInfo structure                      */
    /*---------------------------------------------------*/
    LPTInfo[cInstances-1].pInst = pInst;  /* save adapter instance */

    switch (cInstances-1)
    {
     case 0:
       pInst->cSignature = *((PULONG)("LPT1"));
       break;

     case 1:
       pInst->cSignature = *((PULONG)("LPT2"));
       break;

     case 2:
       pInst->cSignature = *((PULONG)("LPT3"));
       break;
    } /* endswitch */


    /*---------------------------------------------------*/
    /* Get bus info from initialization request packet   */
    /* Initialize BusType field                          */
    /*---------------------------------------------------*/
    pMInfo = (PMACHINE_CONFIG_INFO)pRPI->InitArgs;
    OFFSETOF(pMInfo) = ((PDDD_PARM_LIST)pMInfo)->machine_config_table;

    if (pMInfo->BusInfo & BUSINFO_MCA)
      BusType = 0x0004;
    else if (pMInfo->BusInfo & BUSINFO_EISA)
           BusType = 0x0002;
         else
           BusType = 0x0001;

    /*---------------------------------------------------*/
    /* Determine parallel port hardware                  */
    /*---------------------------------------------------*/
    if (parDetermineHardware( BusType ))
    {
      /*---------------------------------------------------*/
      /* Allocate GDT selector for data transfers          */
      /*---------------------------------------------------*/
      if (DevHelp_AllocGDTSelector( (PSEL)(&(pInst->GDTSel)), 1 ) != 0 )
        goto parInit_Failed;              /* then fail initialization */

      /*---------------------------------------------------*/
      /* Initialize default timeout values                 */
      /*---------------------------------------------------*/
      pInst->RIdleTimeoutMS = 0x4000;  /* read request timeout */
      pInst->RTimeoutMS = 0x0100;      /* read inter-character timeout */

      pInst->WIdleTimeoutMS = 0x4000;  /* write request timeout */
      pInst->WTimeoutMS = 0x0100;      /* write inter-character timeout */

      /*---------------------------------------------------*/
      /* Initialize default communication modes            */
      /*---------------------------------------------------*/
      pInst->ReqCommMode = PAR_COMPATIBILITY;
      pInst->CurCommMode = PAR_COMPATIBILITY;

      /*---------------------------------------------------*/
      /* Initialize default frame control setting          */
      /*---------------------------------------------------*/
      pInst->CharsPerLine = CPS_80;
      pInst->LinesPerInch = LPI_6;

      /*---------------------------------------------------*/
      /* Initialize last read buffer pointer               */
      /*---------------------------------------------------*/
      pInst->pLastRead = &LastRead[cInstances-1];

      /*----------------------------------------------*/
      /* Set the Control Reg setting and generate a   */
      /* 100us pulse on INIT to force a printer reset.*/
      /*----------------------------------------------*/
      pInst->ControlReg = SPP_CONTROL_SELECT | SPP_CONTROL_INIT;

      IOWrite8( pInst->pIO[0],
                SPP_CONTROL_REG,
                ((UCHAR)(pInst->ControlReg ^ SPP_CONTROL_INVERT)));

      TimeExecutionStall( 200 );

      pInst->ControlReg &= ~SPP_CONTROL_INIT;

      IOWrite8( pInst->pIO[0],
                SPP_CONTROL_REG,
                ((UCHAR)(pInst->ControlReg ^ SPP_CONTROL_INVERT)));

      /*---------------------------------------------------*/
      /* Determine device and associate device with device */
      /* driver and adapter with resource manager.         */
      /*---------------------------------------------------*/
      PARCreateDevice( pInst );

      /*---------------------------------------------------*/
      /* Initialize IRQInfo structure                      */
      /*---------------------------------------------------*/
      pInst->IRQINFOIndex = (UCHAR)(cInstances-1);

      if (PortCB[cInstances-1].hIrqRes)   /* if irq alloc by resource mgr */
      {
        IRQInfo[cInstances-1].IRQLevel = PortCB[cInstances-1].IrqLevel;
      }
      else                                /* no irq from resource mgr */
      {
        SetInstanceFlags ( pInst, F_NO_INTERRUPT );
        IRQInfo[cInstances-1].IRQLevel = 0;
      } /* endif */

      if (pMInfo->BusInfo & BUSINFO_MCA)
        IRQInfo[cInstances-1].Flags = 0x0001; /* IRQ shared */
      else
        IRQInfo[cInstances-1].Flags = 0x0000; /* IRQ not shared */

      switch (cInstances-1)
      {
        case 0:
           IRQInfo[cInstances-1].isrRtn = (NPFN)&parIRQEntry_0;
           break;

        case 1:
           IRQInfo[cInstances-1].isrRtn = (NPFN)&parIRQEntry_1;
           break;

        case 2:
           IRQInfo[cInstances-1].isrRtn = (NPFN)&parIRQEntry_2;
           break;

      } /* endswitch */

      IRQInfo[cInstances-1].pInst = pInst;

    } /* endif */
  } /* endif */

  pRPO->CodeEnd = ((USHORT) &parInit) - 1;       /* set end of code segment */
  pRPO->DataEnd = ((USHORT) &InitDataStart) - 1; /* set end of data segment */
  pRP->Status = STATUS_DONE;
  return;

parInit_Failed:
  if ( pInst )
  {
    DestroyInstance( pInst );
    pInst = 0;
  }
  pRPO->CodeEnd = 0;           /* set end of code segment */
  pRPO->DataEnd = 0;           /* set end of data segment */
  pRP->Status = STDON + STERR + ERROR_I24_GEN_FAILURE;
  return;
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  parDetermineHardware                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Determine the parallel port hardware.           */
/*                                                                    */
/* FUNCTION:  The function of this routine is to determine whether    */
/*            the parallel ports are present in the system and to     */
/*            register their resources with the resource manager.     */
/*                                                                    */
/* NOTES: BIOS data area may report parallel port, but if resource    */
/*        manager does not give it to us, this routine returns false. */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  parDetermineHardware                                 */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT          BusType  - bus type                        */
/*                                                                    */
/* EXIT-NORMAL: TRUE - parallel port found (see PortCB structure)     */
/*                                                                    */
/* EXIT-ERROR:  FALSE - no parallel port found                        */
/*                                                                    */
/* EFFECTS: PortCount, IRQCount, PortCB, IrqCB, DetectCB              */
/*                                                                    */
/* INTERNAL REFERENCES:  PARAllocPort                                 */
/*                       PARAllocIRQ                                  */
/*                       DetectIRQ                                    */
/*                       DefaultIRQ                                   */
/*                       PARCreateAdapter                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMDeallocResource                            */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
BOOL parDetermineHardware( USHORT BusType )
{
  parInstance_t *pInst;
  FPBIOSDATAAREA pBIOSDataArea;     /* ptr to BIOS data area */
  BOOL         DeviceFound = FALSE; /* device found flag */
  USHORT       i, j;                /* loop control variables */

  /*---------------------------------------------------*/
  /* Look in BIOS data area for all of the parallel    */
  /* port base addresses present in the system (no     */
  /* need to poke addresses since BIOS has already     */
  /* done this for us).                                */
  /*---------------------------------------------------*/
  pBIOSDataArea = MAKEP(0x40, 8);
  i = cInstances-1;

  if ((pBIOSDataArea->parBaseAddr[i] == PORT3BC) ||
      (pBIOSDataArea->parBaseAddr[i] == PORT378) ||
      (pBIOSDataArea->parBaseAddr[i] == PORT278))
  {
    PortCB[i].PortAddr = pBIOSDataArea->parBaseAddr[i];
    /*---------------------------------------------------*/
    /* Reserve this parallel port base address found in  */
    /* the BIOS data area with Resource Manager          */
    /*---------------------------------------------------*/
    if (!PARAllocPort ( PortCB[i].PortAddr, &PortCB[i].hPortRes ))
    {
      PortCount++;
      DeviceFound = TRUE;
    }
  }


  if (DeviceFound)
  {
    pInst = LPTInfo[i].pInst;  /* get pointer to adapter instance */
    pInst->pIO[0] = PortCB[i].PortAddr;

    /*-----------------------------------------------------*/
    /* Save initial value of status and control registers. */
    /*-----------------------------------------------------*/
    pInst->StatusReg = IORead8( pInst->pIO[0], SPP_STATUS_REG );
    pInst->StatusReg ^=  SPP_STATUS_INVERT;

    pInst->ControlReg = IORead8( pInst->pIO[0], SPP_CONTROL_REG );
    pInst->ControlReg ^= SPP_CONTROL_INVERT;

    /*---------------------------------------------------*/
    /* Determine if the port is an Enhanced Capabilities */
    /* Parallel port.  If an ECP parallel port is found  */
    /* then additional parallel port registers must be   */
    /* reserved with Resource Manager.                   */
    /*---------------------------------------------------*/
    if (DetectECP( PortCB[i].PortAddr, &PortCB[i].ECPPortAddr ))
    {
      if (PARAllocPort ( PortCB[i].ECPPortAddr, &PortCB[i].hECPPortRes ))
        PortCB[i].ECPPortAddr = 0x0000;
      else
      {
        /*---------------------------------------------------*/
        /* Initialize pio object structure                   */
        /*---------------------------------------------------*/
        pInst->pIO[1] = PortCB[i].ECPPortAddr;

        /*---------------------------------------------------*/
        /* Save initial value of Extended Control Register.  */
        /*---------------------------------------------------*/
        pInst->ExtControlReg  = IORead8( pInst->pIO[1], EXT_CONTROL_REG );
        pInst->ExtControlReg ^= EXT_CONTROL_INVERT;

        /*---------------------------------------------------*/
        /* Determine FIFO depth, read, and write thresholds. */
        /* DetermineFIFOThresholds needs pio object.         */
        /*---------------------------------------------------*/
        DetermineFIFOThresholds( pInst );
        if ( (pInst->FIFOReadThresh  >= 4) && ( pInst->FIFOWriteThresh >= 4) )
        {
          SetInstanceFlags( pInst, F_FIFO_SUPPORT );
        }
        else
        {
          ResetInstanceFlags( pInst, F_FIFO_SUPPORT );
          pInst->pIO[1] = 0;
        }
      }
    }

    /*---------------------------------------------------*/
    /* Reserve all possible IRQs With Resource Manager   */
    /*---------------------------------------------------*/
    for (j=0; j < MAX_IRQS; j++)
    {
      if (!PARAllocIRQ ( BusType, IrqCB[j].IrqLevel, &IrqCB[j].hIrqRes))
        IRQCount++;

    } /* endfor */

    /*---------------------------------------------------*/
    /* Using the list of IRQ levels reserved by us with  */
    /* resource manager, determine the IRQ level         */
    /* configured for each parallel port base address.   */
    /*---------------------------------------------------*/

    /*---------------------------------------------------*/
    /* Try to dynamically determine the IRQ level        */
    /*---------------------------------------------------*/
    PortCB[i].IrqLevel = DetectIRQ( PortCB[i].PortAddr);

    /*---------------------------------------------------*/
    /* If IRQ could not be dynamically determined, set   */
    /* default (best guess) IRQ level                    */
    /*---------------------------------------------------*/
    if (PortCB[i].IrqLevel == 0)          /* if irq not determined */
      PortCB[i].IrqLevel = DefaultIRQ( PortCB[i].PortAddr, BusType);

    /*---------------------------------------------------*/
    /* Save irq resource handle in PortCB structure.     */
    /* Set irq needed flag in IrqCB structure.           */
    /*---------------------------------------------------*/
    for (j=0; j < MAX_IRQS; j++)          /* loop through IrqCB array */
    {
       if (PortCB[i].IrqLevel == IrqCB[j].IrqLevel)
       {
         PortCB[i].hIrqRes = IrqCB[j].hIrqRes;
         IrqCB[j].fIrqNeeded = TRUE;
       }
    }

    /*---------------------------------------------------*/
    /* Return any IRQ resources previously allocated but */
    /* not needed by parallel port device driver         */
    /*---------------------------------------------------*/
    for (j=0; j < MAX_IRQS; j++)          /* loop through IrqCB array */
    {
      if ((IrqCB[j].hIrqRes) &&           /* if res mgr gave us irq */
          (!IrqCB[j].fIrqNeeded))         /* and irq is not needed */
      {
        RMDeallocResource( hDriver, IrqCB[j].hIrqRes);
        IrqCB[j].hIrqRes = 0;
      }
    } /* endfor */

    /*---------------------------------------------------*/
    /* Associate hardware resources with adapter and     */
    /* adapter with device driver.                       */
    /*---------------------------------------------------*/
    PARCreateAdapter( BusType, i, &PortCB[i].hAdapter, PortCB[i].hPortRes,
                      PortCB[i].hIrqRes );

  }
  return (DeviceFound);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  DetectECP                                        */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detect whether port is an ECP parallel port.    */
/*                                                                    */
/* FUNCTION:  The function of this routine is to detect whether port  */
/*            is an ECP parallel port.                                */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  DetectECP                                            */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT       PortAddr    - parallel port base address      */
/*         USHORT       ECPPortAddr - ptr to ECP base address (return)*/
/*                                                                    */
/* EXIT-NORMAL:  TRUE - port is ECP Port                              */
/*                                                                    */
/* EXIT-ERROR:  FALSE - port is not ECP Port                          */
/*                                                                    */
/* EFFECTS: Set ECP extended base address to base + 0x400, else 0.    */
/*                                                                    */
/* INTERNAL REFERENCES:  IORead8                                      */
/*                       IOWrite8                                     */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
BOOL DetectECP( USHORT PortAddr, USHORT *ECPPortAddr )
{
  USHORT   ExtBaseAddr;      /* ECP extended registers base address */
  BOOL     returncode = FALSE;
  UCHAR    ECPchar;
  UCHAR    ECROriginalbyte;
  UCHAR    ECRModebyte;
  UCHAR    ECRAfterbyte;

  /*---------------------------------------------------*/
  /* Calculate ECP base address.  It is defined as the */
  /* SPP/EPP base address plus 0x400.                  */
  /*---------------------------------------------------*/
  ExtBaseAddr = PortAddr + 0x400;

  /*---------------------------------------------------*/
  /* Read byte from ECR (Extended Control Register)    */
  /*---------------------------------------------------*/
  ECROriginalbyte = IORead8(ExtBaseAddr, EXT_CONTROL_REG);

  /*---------------------------------------------------*/
  /* If FIFO is not empty or FIFO is full then it is   */
  /* not an ECP parallel port.                         */
  /*---------------------------------------------------*/
  if ( !( ECROriginalbyte & EXT_CONTROL_FIFOEMPTY ) ||
        ( ECROriginalbyte & EXT_CONTROL_FIFOFULL ) )
  {
    *ECPPortAddr = 0;
    return ( returncode );
  }

  /*---------------------------------------------------*/
  /* If DCR == ECR then not an ECP parallel port.      */
  /*---------------------------------------------------*/
  if ( (IORead8(PortAddr, SPP_CONTROL_REG) & ~SPP_CONTROL_RESERVED) ==
       (                   ECROriginalbyte & ~SPP_CONTROL_RESERVED) )
  {
    *ECPPortAddr = 0;
    return ( returncode );
  }

  IOWrite8( ExtBaseAddr,
            EXT_CONTROL_REG,
            EXT_CONTROL_MODE1 | EXT_CONTROL_NOERRIRQ | EXT_CONTROL_FIFOIRQ );

  if ( IORead8(ExtBaseAddr, EXT_CONTROL_REG) == 0x35 )
  {
    ECRModebyte = (UCHAR)((ECROriginalbyte & ~EXT_CONTROL_MODE) | (EXT_CONTROL_MODE7));

    /*-------------------------------------------------------*/
    /* Write TESTMODE 7 to ECR (Extended Control Register)   */
    /*-------------------------------------------------------*/
    IOWrite8(ExtBaseAddr, EXT_CONTROL_REG, ECRModebyte);

    /*---------------------------------------------------*/
    /* Read again from Extended Control Register.        */
    /*---------------------------------------------------*/
    ECRAfterbyte = IORead8(ExtBaseAddr, EXT_CONTROL_REG);

    if ( ECRAfterbyte == ECRModebyte)  /* Sanity check OK, we are in new mode */
    {
      /* Read Configuration Register A - offset 0. */
      ECPchar = IORead8(ExtBaseAddr, ECP_CFGA_REG);

      /*----------------------------------------------------*/
      /* If byte read from Configuration register A matches */
      /* the chip specs, we have an ECP parallel port.      */
      /*----------------------------------------------------*/
      if ( ( ECPchar == 0x14 ) ||          /* National Semi SIO chip */
           ( ECPchar == 0x10 ) )           /* SMC SIO chip           */
      {
        returncode = TRUE;
      }
    }
    /* Attempt to return the "ECR" to its previous state. */
    IOWrite8(ExtBaseAddr, EXT_CONTROL_REG, ECROriginalbyte);
  } /* END of if ( ECRbyte == 0x15 ) */

  if ( returncode == TRUE)
    *ECPPortAddr = ExtBaseAddr;
  else
    *ECPPortAddr = 0;

  return ( returncode );
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  DetectIRQ                                        */
/*                                                                    */
/* DESCRIPTIVE NAME:  Detect the IRQ configured for the parallel port.*/
/*                                                                    */
/* FUNCTION:  The function of this routine is to detect the hardware  */
/*            interrupt level configured for the parallel port and    */
/*            return it.                                              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  DetectIRQ                                            */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT       PortAddr    - parallel port base address      */
/*                                                                    */
/* EXIT-NORMAL:  7 - parallel port configured for IRQ7                */
/*               5 - parallel port configured for IRQ5                */
/*                                                                    */
/* EXIT-ERROR:  0 - parallel port configuration unknown               */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  IORead8                                      */
/*                       IOWrite8                                     */
/*                       IODelay                                      */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_GetDOSVar                            */
/*                       DevHelp_SetIRQ                               */
/*                       DevHelp_TickCount                            */
/*                       DevHelp_ResetTimer                           */
/*                       DevHelp_UnSetIRQ                             */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT DetectIRQ( USHORT PortAddr )
{
   struct InfoSegGDT far *pInfoSegGDT;    /* 16:16 ptr to global info seg */
   ULONG  far *pInfoSegSel;               /* selector to global info seg */
   USHORT i;                              /* loop control variable */
   BOOL   fSetIRQ5, fSetIRQ7 = FALSE;     /* SetIRQ flags */
   USHORT StatusReg = 0;                  /* status register */

   DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID)&pInfoSegSel);
   pInfoSegGDT = MAKEP(*pInfoSegSel, 0);

   /*---------------------------------------------------*/
   /* Setup detect control block for processing at      */
   /* interrupt time.                                   */
   /*---------------------------------------------------*/
   DetectCB.WaitForIRQ = TRUE;
   DetectCB.PortAddr = PortAddr;
   DetectCB.DetectIRQ = 0x0000;

   /*---------------------------------------------------*/
   /* Register our hardware interrupt service routines  */
   /* with the hardware interrupt manager.              */
   /*---------------------------------------------------*/
   for (i=0; i < MAX_IRQS; i++)           /* loop through IrqCB array */
   {
     if ((IrqCB[i].IrqLevel == IRQ7) &&   /* if IRQ7 and */
         (IrqCB[i].hIrqRes))              /* res mgr gave us IRQ7 */
       fSetIRQ7 = (~(BOOL) DevHelp_SetIRQ((NPFN)IRQ7ISR, IRQ7, 0));
     else if ((IrqCB[i].IrqLevel == IRQ5) && /* if IRQ5 and */
              (IrqCB[i].hIrqRes))            /* res mgr gave us IRQ5 */
            fSetIRQ5 = (~(BOOL) DevHelp_SetIRQ((NPFN)IRQ5ISR, IRQ5, 0));
   }

   if ((!fSetIRQ7) &&                     /* if SetIRQ for IRQ7 and IRQ5 */
       (!fSetIRQ5))                       /* failed, then dynamic detection */
     return(DetectCB.DetectIRQ);          /* impossible */

   /*---------------------------------------------------*/
   /* Register our timer handler with timer services.   */
   /*---------------------------------------------------*/
   DevHelp_TickCount((NPFN)PARTimeout, 10000 / pInfoSegGDT->SIS_ClkIntrvl);

   /*---------------------------------------------------*/
   /* Check status port for normal status.              */
   /*---------------------------------------------------*/
   StatusReg = IORead8(PortAddr, STATUS_REG);
   StatusReg ^= STATUS_INVERT;
   StatusReg &= ~STATUS_UNUSED;
   if (StatusReg == STATUS_READY)         /* ready status */
   {
      IOWrite8(PortAddr, DATA_REG, NULL); /* NULL = non printable char */

      CLI();                              /* disable interrupts */
      IOWrite8(PortAddr, CTRL_REG, CONTROL_IRQEN | CONTROL_SELECT |
                                   CONTROL_INIT | CONTROL_STROBE);

      IODelay();                          /* strobe width */
      IODelay();                          /* strobe width */

      IOWrite8(PortAddr, CTRL_REG, CONTROL_IRQEN | CONTROL_SELECT |
                                   CONTROL_INIT);
      STI();                              /* enable interrupts */

      /*---------------------------------------------------*/
      /* Wait for hardware interrupt or timeout to occur.  */
      /*---------------------------------------------------*/
      while (DetectCB.WaitForIRQ)
      {
      } /* endwhile */
   }

   /*---------------------------------------------------*/
   /* Deregister our timer handler from timer services. */
   /*---------------------------------------------------*/
   DevHelp_ResetTimer((NPFN)PARTimeout);

   /*---------------------------------------------------*/
   /* Disable IRQ Enable bit in control register.       */
   /*---------------------------------------------------*/
   IOWrite8(PortAddr, CTRL_REG, CONTROL_SELECT | CONTROL_INIT);

   /*---------------------------------------------------*/
   /* Deregister our hardware interrupt service routines*/
   /* with the hardware interrupt manager.              */
   /*---------------------------------------------------*/
   if (fSetIRQ7)                          /* if SetIRQ was successful */
     DevHelp_UnSetIRQ(IRQ7);

   if (fSetIRQ5)                          /* if SetIRQ was successful */
     DevHelp_UnSetIRQ(IRQ5);

   return(DetectCB.DetectIRQ);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  DefaultIRQ                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Determine the default IRQ to use for the        */
/*                    parallel port.                                  */
/*                                                                    */
/* FUNCTION:  The function of this routine is to determine the        */
/*            hardware interrupt level to use for the parallel port   */
/*            based upon the industry standard default settings.      */
/*                                                                    */
/* NOTES:  This routine does not take into account whether the device */
/*         driver received access to the irq from resource manager.   */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  DefaultIRQ                                           */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT       PortAddr    - parallel port base address      */
/*         USHORT       BusType     - type of bus (ISA, EISA, MC, etc)*/
/*                                                                    */
/* EXIT-NORMAL:  7 - parallel port configured for IRQ7                */
/*               5 - parallel port configured for IRQ5                */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT DefaultIRQ( USHORT PortAddr, USHORT BusType )
{
   USHORT   DefIRQ = 0;       /* default IRQ level */

   /*---------------------------------------------------*/
   /* The following table describes the interrupt level */
   /* resource being reserved with resource manager for */
   /* each base parallel port address.                  */
   /*                                                   */
   /* All known micro channel parallel ports use IRQ7.  */
   /* Very few EISA parallel ports. Many EISA systems   */
   /* have ISA parallel ports. EISA and ISA parallel    */
   /* ports could use either IRQ5 or IRQ7. This table   */
   /* represents a "typical" default hardware           */
   /* configuration.  It does not work for every case.  */
   /*                                                   */
   /* Dynamic IRQ determination is not possible without */
   /* a powered on parallel port attached device.       */
   /*                                                   */
   /* +------------+-----------------------+            */
   /* | Base Addr  |        Bus Type       |            */
   /* +------------+-------+--------+------+            */
   /* |            |  ISA  |  EISA  |  MC  |            */
   /* +------------+-------+--------+------+            */
   /* |   3BC      |   7   |   7    |  7   |            */
   /* +------------+-------+--------+------+            */
   /* |   378      |   7   |   7    |  7   |            */
   /* +------------+-------+--------+------+            */
   /* |   278      |   5   |   5    |  7   |            */
   /* +------------+-------+--------+------+            */
   /*---------------------------------------------------*/

   if ((PortAddr == PORT3BC) ||
       (PortAddr == PORT378) ||
       (BusType == 0x0004))         /* micro channel bus */
     DefIRQ = IRQ7;
   else
     DefIRQ = IRQ5;

   return(DefIRQ);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  IRQ7ISR                                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  IRQ7 interrupt service routine                  */
/*                                                                    */
/* FUNCTION:  The function of this routine is to service IRQ7 hardware*/
/*            interrupts.                                             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  IRQ7ISR                                              */
/*    LINKAGE:  CALL FAR                                              */
/*                                                                    */
/* INPUT:  NONE                                                       */
/*                                                                    */
/* EXIT-NORMAL:  NC - my device generated interrupt (claim int.)      */
/*                                                                    */
/* EXIT-ERROR:  CY - my device did not generate interrupt             */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void FAR IRQ7ISR()
{
   /*---------------------------------------------------*/
   /* Read parallel port status register to clear       */
   /* interrupt condition at device.                    */
   /*---------------------------------------------------*/
   IORead8 (DetectCB.PortAddr, STATUS_REG);

   DetectCB.DetectIRQ = IRQ7;             /* set interrupt level to IRQ7 */
   DetectCB.WaitForIRQ = FALSE;           /* set process complete flag */

   CLI();                                 /* disable interrupts */
   DevHelp_EOI( IRQ7 );                   /* issue end of interrupt */
   CLC();                                 /* Clear the carry flag to indicate */
                                          /* to kernel that we have claimed   */
                                          /* (serviced) the interrupt.        */
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  IRQ5ISR                                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  IRQ5 interrupt service routine                  */
/*                                                                    */
/* FUNCTION:  The function of this routine is to service IRQ5 hardware*/
/*            interrupts.                                             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  IRQ5ISR                                              */
/*    LINKAGE:  CALL FAR                                              */
/*                                                                    */
/* INPUT:  NONE                                                       */
/*                                                                    */
/* EXIT-NORMAL:  NC - my device generated interrupt (claim int.)      */
/*                                                                    */
/* EXIT-ERROR:  CY - my device did not generate interrupt             */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void FAR IRQ5ISR()
{
   /*---------------------------------------------------*/
   /* Read parallel port status register to clear       */
   /* interrupt condition at device.                    */
   /*---------------------------------------------------*/
   IORead8 (DetectCB.PortAddr, STATUS_REG);

   DetectCB.DetectIRQ = IRQ5;             /* set interrupt level to IRQ5 */
   DetectCB.WaitForIRQ = FALSE;           /* set process complete flag */

   CLI();                                 /* disable interrupts */
   DevHelp_EOI( IRQ5 );                   /* issue end of interrupt */
   CLC();                                 /* Clear the carry flag to indicate */
                                          /* to kernel that we have claimed   */
                                          /* (serviced) the interrupt.        */

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  PARTimeout                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Parallel port transmission timeout routine      */
/*                                                                    */
/* FUNCTION:  The function of this routine is to process a timeout    */
/*            when a hardware interrupt does not occur.               */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  PARTimeout                                           */
/*    LINKAGE:  CALL FAR                                              */
/*                                                                    */
/* INPUT:  NONE                                                       */
/*                                                                    */
/* EXIT-NORMAL:  None                                                 */
/*                                                                    */
/* EXIT-ERROR:  None                                                  */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void FAR PARTimeout()
{
   /*---------------------------------------------------*/
   /* The hardware interrupt never occurred.            */
   /*---------------------------------------------------*/
   DetectCB.DetectIRQ = 0x0000;           /* set interrupt level to unknown */
   DetectCB.WaitForIRQ = FALSE;           /* set process complete flag */

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  PARAllocPort                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Claim a port resource from resource manager     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to allocate a port      */
/*            resource from the resource manager.                     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  PARAllocPort                                         */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT       baseport    - parallel port base address      */
/*         PHRESOURCE   phResource  - far pointer to resource handle  */
/*                                                                    */
/* EXIT-NORMAL:  RMAllocResource return code                          */
/*                                                                    */
/* EXIT-ERROR:  RMAllocResource return code                           */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMAllocResource                              */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT PARAllocPort( USHORT baseport, PHRESOURCE phResource)
{

   RESOURCESTRUCT Resource;

   if (baseport == 0x3BC)
     Resource.IOResource.NumIOPorts = 0x0004;
   else
     Resource.IOResource.NumIOPorts = 0x0008;

   Resource.ResourceType              = RS_TYPE_IO;
   Resource.IOResource.BaseIOPort     = baseport;
   Resource.IOResource.IOFlags        = RS_IO_MULTIPLEXED;
   Resource.IOResource.IOAddressLines = DECODE_WIDTH;

   /*---------------------------------------------------*/
   /* Allocate Port Resource                            */
   /*---------------------------------------------------*/
   return( RMAllocResource( hDriver, phResource, &Resource));

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  PARAllocIRQ                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Claim an IRQ resource from resource manager     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to allocate an IRQ      */
/*            resource from the resource manager.                     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  PARAllocIRQ                                          */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT       bustype     - type of bus                     */
/*         USHORT       IRQlevel    - parallel port irq level         */
/*         PHRESOURCE   phResource  - far pointer to resource handle  */
/*                                                                    */
/* EXIT-NORMAL:  RMAllocResource return code                          */
/*                                                                    */
/* EXIT-ERROR:  RMAllocResource return code                           */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMAllocResource                              */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT PARAllocIRQ( USHORT bustype, USHORT IRQlevel, PHRESOURCE phResource)
{

   RESOURCESTRUCT Resource;

   switch (bustype)
   {
     case 0x0001:                                           /* ISA Bus */
      Resource.IRQResource.IRQFlags  = RS_IRQ_MULTIPLEXED;
      break;

     case 0x0002:                                           /* EISA Bus */
      Resource.IRQResource.IRQFlags  = RS_IRQ_SHARED;
      break;

     case 0x0004:                                           /* MC Bus */
      Resource.IRQResource.IRQFlags  = RS_IRQ_SHARED;
      break;

   default:
      Resource.IRQResource.IRQFlags  = RS_IRQ_EXCLUSIVE;
      break;

   } /* endswitch */

   /*------------------------------------------------*/
   /* Request IRQ Resource from RM......             */
   /*------------------------------------------------*/
   Resource.ResourceType          = RS_TYPE_IRQ;
   Resource.IRQResource.IRQLevel  = IRQlevel;
   Resource.IRQResource.PCIIrqPin = RS_PCI_INT_NONE;
   Resource.IRQResource.Reserved  = 0;
   Resource.IRQResource.pfnIntHandler = 0;

   /*---------------------------------------------------*/
   /* Allocate IRQ Resource                             */
   /*---------------------------------------------------*/
   return( RMAllocResource( hDriver, phResource, &Resource));

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  PARCreateAdapter                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Tells resource manager to associate resources   */
/*                    to an adapter and an adapter to a driver        */
/*                                                                    */
/* FUNCTION:  The function of this routine is to create an adapter    */
/*            with the resource manager.                              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  PARCreateAdapter                                     */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT       bustype     - type of bus                     */
/*                       0x0001     = ISA                             */
/*                       0x0002     = EISA                            */
/*                       0x0003     = MC                              */
/*         USHORT       adaptnum    - parallel port adapter number    */
/*                        0x0001    = first adapter                   */
/*                        0x0002    = second adapter                  */
/*                        0x0003    = third adapter                   */
/*         PHRESOURCE   phAdapter   - far pointer to adapter handle   */
/*         HRESOURCE    hResPort    - handle to port resource         */
/*         HRESOURCE    hResIRQs    - handle to irq resource          */
/*                                                                    */
/* EXIT-NORMAL:  RMCreateAdapter return code                          */
/*                                                                    */
/* EXIT-ERROR:  RMCreateAdapter return code                           */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMCreateAdapter                              */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT PARCreateAdapter( USHORT bustype, USHORT adaptnum, PHADAPTER phAdapter,
                         HRESOURCE hResPort, HRESOURCE hResIRQ)
{
   UCHAR       ResourceBuf[ 12 ];
   PAHRESOURCE pResourceList = (PAHRESOURCE)ResourceBuf;
   ADJUNCT     AdjAdaptNum;

   if (hResIRQ == NULL)                   /* if irq not allocated */
     pResourceList->NumResource = 1;      /* only port resource */
   else
     pResourceList->NumResource = 2;      /* port and irq resource */

   pResourceList->hResource[0] = hResPort;
   pResourceList->hResource[1] = hResIRQ;

   switch (bustype)
   {
     case 0x0001:
      AdapterStruct.HostBusType = AS_HOSTBUS_ISA;
      break;

     case 0x0002:
      AdapterStruct.HostBusType = AS_HOSTBUS_EISA;
      break;

     case 0x0004:
      AdapterStruct.HostBusType = AS_HOSTBUS_uCHNL;
      break;

   default:
      AdapterStruct.HostBusType = AS_HOSTBUS_UNKNOWN;
      break;

   } /* endswitch */

   /*---------------------------------------------------*/
   /* Initialize AdjunctList                            */
   /*---------------------------------------------------*/

   AdjAdaptNum.pNextAdj       = NULL;
   AdjAdaptNum.AdjLength      = sizeof(ADJUNCT);
   AdjAdaptNum.AdjType        = ADJ_ADAPTER_NUMBER;
   AdjAdaptNum.Adapter_Number = adaptnum;

   AdapterStruct.pAdjunctList = &AdjAdaptNum;

   /*---------------------------------------------------*/
   /* Create Adapter Handle                             */
   /*---------------------------------------------------*/
   return ( RMCreateAdapter( hDriver,
                             phAdapter,
                             &AdapterStruct,
                             NULL,
                             pResourceList ));
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  PARCreateDevice                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Determine the parallel port device.             */
/*                                                                    */
/* FUNCTION:  The function of this routine is to determine the        */
/*            device attached to the parallel port and register a     */
/*            device structure with resource manager.                 */
/*                                                                    */
/* NOTES:  The device structure is created only when a IEEE-1284      */
/*         compliant device is attached to a parallel port and the    */
/*         device is powered on.                                      */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  PARCreateDevice                                      */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - adapter instance                */
/*                                                                    */
/* EXIT-NORMAL:  N/A                                                  */
/*                                                                    */
/* EXIT-ERROR:   N/A                                                  */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  parDeviceIdQuery                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  RMCreateDevice                               */
/*                       DevHelp_AllocReqPacket                       */
/*                       DevHelp_UnLock                               */
/*                       DevHelp_FreeReqPacket                        */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void PARCreateDevice( parInstance_t *pInst )
{
  PRPH         pReqPkt;     /* ptr to allocated IOCTL request packet */
  PRPH         pInitReqPkt; /* ptr to frame control request packet */
  USHORT       i;           /* index variable */

  /*---------------------------------------------------*/
  /* Allocate and initialize IOCtl request packet.     */
  /*---------------------------------------------------*/
  if (!DevHelp_AllocReqPacket( WAIT_NOT_ALLOWED,
                               (PBYTE FAR *) &pReqPkt ))
  {
    ((PRP_GENIOCTL)pReqPkt)->rph.Cmd = CMDGenIOCTL;
    ((PRP_GENIOCTL)pReqPkt)->rph.Status = 0;
    ((PRP_GENIOCTL)pReqPkt)->Category = IOC_PC;
    ((PRP_GENIOCTL)pReqPkt)->Function = IOLR_ID;
    ((PRP_GENIOCTL)pReqPkt)->DataPacket = (PUCHAR)&QryIDBuf;
    ((PRP_GENIOCTL)pReqPkt)->DataLen = sizeof(QryIDBuf);

    pInitReqPkt = pInst->pRP;                /* temp save init req pkt */
    pInst->pRP = pReqPkt;                    /* store IOCtl RP in instance */

    /*------------------------------------------------------*/
    /* Query the device id of parallel port attached device */
    /*------------------------------------------------------*/
    pInst->ReqCommMode = PAR_NIBBLE;         /* set comm mode for query */
    parDeviceIDQuery( pInst );
    pInst->ReqCommMode = PAR_COMPATIBILITY;  /* reset default comm mode */

    pInst->pRP = pInitReqPkt;                /* restore init req pkt in inst */

    if (!(((PRP_GENIOCTL)pReqPkt)->rph.Status & STERR))
    {
      /*---------------------------------------------------*/
      /* Update DeviceStruct with IEEE-1284 device id.     */
      /* (Length in first two bytes).                      */
      /*---------------------------------------------------*/
      DeviceStruct.DevDescriptName = getModel( (PSZ)&QryIDBuf+2 );

      /*---------------------------------------------------*/
      /* Associate device with device driver and adapter.  */
      /*---------------------------------------------------*/
      i = cInstances-1;
      RMCreateDevice( hDriver, &PortCB[i].hDevice, &DeviceStruct,
                      PortCB[i].hAdapter, NULL );
    }

    DevHelp_FreeReqPacket( (PBYTE)pReqPkt );
  }
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  getModel                                         */
/*                                                                    */
/* DESCRIPTIVE NAME:  Find and return MODEL: phrase from device id    */
/*                                                                    */
/* FUNCTION:  The function of this routine is to find and return      */
/*            the MODEL: phrase from the IEEE-1284 device id string.  */
/*                                                                    */
/* NOTES:  The Device ID string format is key:value{,value};          */
/*                                                                    */
/* CONTEXT:                                                           */
/*                                                                    */
/* ENTRY POINT:  getModel                                             */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  PSZ     pDevID - pointer to device id string               */
/*                                                                    */
/* EXIT-NORMAL:  returns MODEL: string in asciiz format               */
/*                                                                    */
/* EXIT-ERROR:   returns null string                                  */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  strncmp                                      */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
PSZ getModel( PSZ pDevID )
{
  PSZ        pTmp1;                       /* temporary pointer */
  PSZ        pTmp2;                       /* temporary pointer */

  pTmp1 = pDevID;
  while (*pTmp1 != NULL)
  {
    if ( (stringncmp( pTmp1, Compare1, sizeof(Compare1)-1 )) ||
         (stringncmp( pTmp1, Compare2, sizeof(Compare2)-1 )) )
    {
      while (*pTmp1 != ':')               /* remove key, leave value */
        pTmp1++;
      pTmp1++;

      pTmp2 = pTmp1;
      while (( *pTmp2 != ';' ) &&         /* go to end of value */
             ( *pTmp2 != NULL ))          /* or end of buffer */
        pTmp2++;
      *pTmp2 = NULL;                      /* make value asciiz */

      return(pTmp1);
    }

    while (( *pTmp1 != NULL ) &&        /* not end of string and */
           ( *pTmp1 != ';' ))           /* not end of phrase */
      pTmp1++;                          /* increment pointer to next phrase */

    if (*pTmp1 == ';')                  /* if now at end of phrase */
      pTmp1++;                          /* increment pointer passed delimiter */
  }
  return (pTmp1);

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  stringncmp                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Compare 2 strings for N length.                 */
/*                                                                    */
/* FUNCTION:  The function of this routine is to compare two strings  */
/*            for N positions to determine whether they are equal.    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT:                                                           */
/*                                                                    */
/* ENTRY POINT:  stringncmp                                           */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  PSZ     s1    - pointer to string 1                        */
/*         PSZ     s2    - pointer to string 2                        */
/*         USHORT  Length - length of characters to compare           */
/*                                                                    */
/* EXIT-NORMAL:  TRUE - strings are identical                         */
/*                                                                    */
/* EXIT-ERROR:   FALSE - strings are not identical                    */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
BOOL stringncmp( PSZ s1, PSZ s2, USHORT Length )
{
  USHORT       i;          /* loop control variable */

  for ( i = 0; i < Length ; i++ )      /* compare strings (length positions) */
  {
     if ( *s1 == *s2 )                 /* if chars are the same */
     {
       s1++;                           /* point to next char */
       s2++;
     }
     else                              /* else strings are not the same */
       return ( FALSE );
  } /* endfor */

  return ( TRUE );                     /* strings are identical */
}


