#define INCL_ERROR_H

#include <os2.h>
#include <psd.h>
#include <ebi2.h>


/*
 *  Global Variables
 */

P_F_2    router = 0;
char    *pParmString = 0;

PLMA    *pPSDPLMA = 0;
ulong_t  sizePLMA = 0;

char    *BiosAlias = 0;
ebi2Sig *sig;
EBI2     ebi2CallTable;
void    **IOTable;

ulong_t  AdvIntMode = 0;
ulong_t  Bypass = 0;
char     BypassString[] = "/BYPASS";

uchar_t  SpinLock = 0;

ulong_t  cPhysProcs = 1;
ulong_t  cUsableProcs = 0;

procConfigData procConfig[EBI2_MAXPROCESSORS];


/***  AcquireSpinLock - AcquireSpinLock
 *
 *    This function acquires a spin lock
 *
 *    ENTRY   None
 *
 *    EXIT    EFLAGS
 *
 */

void AcquireSpinLock(void) {

   _asm {
       xor    al,al
       mov    ah,-1
aspl1: cmp    SpinLock,al
       jne    short aspl1
       xchg   SpinLock,ah
       or     ah,ah
       jnz    short aspl1
   }
}

/***  ReleaseSpinLock - ReleaseSpinLock
 *
 *    This function releases a spin lock
 *
 *    ENTRY   None
 *
 *    EXIT    EFLAGS
 *
 */

void ReleaseSpinLock(void) {
   SpinLock = 0;
}

/***  InByte - Read a byte from a port
 *
 *    This function reads a byte from a specified port
 *
 *    ENTRY   port - port number to read from
 *
 *    EXIT    data read
 *
 */

ulong_t InByte(ulong_t port) {

   ulong_t data;

   _asm {
      mov   dx,port
      in    al,dx
      movzx eax,al
      mov   data,eax
   };

   return (data);
}


/***  _strcmp - Compares 2 strings
 *
 *    This function compares 2 strings
 *
 *    ENTRY   s1 - pointer to string
 *            s2 - pointer to string
 *
 *    EXIT    0 - Strings are identical
 *
 */

ulong_t _strcmp(char *s1, char *s2) {
   _asm {
      pushf
      cld

      mov  edi,s1
      mov  ecx,-1
      xor  ax,ax
      repne scasb
      not  ecx

      mov  esi,s1
      mov  edi,s2
      rep  cmpsb
      mov  eax,0
      jz   short sc1
      mov  eax,1

sc1:  popf
   }
}

/***  Disable - Disable interrupts
 *
 *    This function disables interrupts, and returns
 *    the original state of eflags
 *
 *    ENTRY   None
 *
 *    EXIT    EFLAGS
 *
 */

ulong_t Disable(void) {

   ulong_t eflags;

   _asm {
      pushfd
      pop   eax
      mov   eflags,eax
      cli
   };

   return (eflags);
}


/***  Enable - Restore the state of eflags
 *
 *    This function restores the state of eflags
 *
 *    ENTRY   eflags - state of eflags to restore
 *
 *    EXIT    None
 *
 */

void Enable(ulong_t eflags) {

   _asm {
      push  eflags
      popfd
   };

   return;
}


/***  Install - Install PSD
 *
 *    This function checks to see if this PSD is installable on the
 *    current platform.
 *
 *    ENTRY   pinstall - pointer to an INSTALL structure
 *
 *    EXIT    Return Code
 *
 */

ulong_t Install(INSTALL *pinstall) {

   VMALLOC vmac;
   int i;
   ulong_t rc;

   /* Setup Global variables */

   router = pinstall->pPSDHlpRouter;
   pParmString = pinstall->pParmString;
   pPSDPLMA = (void *)pinstall->pPSDPLMA;
   sizePLMA = pinstall->sizePLMA;

   if (pParmString) {

      while (*pParmString == ' ')
         pParmString++;

      if (_strcmp(BypassString, pParmString) == 0)
         Bypass = 1;
   }

   vmac.addr = BIOS_SEG << 4;
   vmac.cbsize = _64K;
   vmac.flags = VMALLOC_PHYS;

   /* Map BIOS area */

   if ((rc = PSDHelp(router, PSDHLP_VMALLOC, &vmac)) == NO_ERROR) {

      /* Assume error */

      rc = -1;

      /* Check for EBI2 signature */

      BiosAlias = (char *)vmac.addr;
      sig = (ebi2Sig *)(BiosAlias + EBI2_SIGNATURE_OFF);

      if (sig->sig == EBI2_SIGNATURE) {

         if (sig->seg == BIOS_SEG)
            rc = NO_ERROR;
      }
   }

   return (rc);
}


/***  DeInstall - DeInstall PSD
 *
 *    This function deinstalls the PSD.
 *
 *    ENTRY   None
 *
 *    EXIT    NO_ERROR
 *
 */

ulong_t DeInstall(void) {

   /* Free BIOS mapping */

   PSDHelp(router, PSDHLP_VMFREE, BiosAlias);

   return (NO_ERROR);
}


/***  Init - Initialize the PSD
 *
 *    This function initalizes the PSD.
 *
 *    ENTRY   pinit - pointer to INIT structure
 *
 *    EXIT    Return Code
 *
 */

ulong_t Init(INIT *pinit) {

   VMALLOC  vmac;
   ulong_t *offstTable;
   ulong_t *ebi2table;
   ulong_t  numSlots;
   IOInfoTable *infoTab;
   SET_IRQ  irq;
   int      i;
   ulong_t  rc;

   offstTable = (ulong_t *)(BiosAlias + sig->off);
   ebi2table = (ulong_t *)&ebi2CallTable;

   /* Fixup the ebi2CallTable */

   for (i = 0; i < (sizeof(EBI2) / sizeof(ulong_t)); i++)
       ebi2table[i] = offstTable[i] + (ulong_t)BiosAlias;

   /* Get number of slots */

   if ((rc = ebi2CallTable.GetNumSlots(&numSlots)) == NO_ERROR) {

      /* Allocate temporary storage for the IOInfoTable */

      vmac.cbsize = numSlots * sizeof(IOInfoTable);
      vmac.flags = VMALLOC_FIXED;

      if ((rc = PSDHelp(router, PSDHLP_VMALLOC, &vmac)) == NO_ERROR) {

         infoTab = (IOInfoTable *)vmac.addr;

         /* Get memory mapped I/O information table */

         if ((rc = ebi2CallTable.GetMMIOTable(infoTab)) == NO_ERROR) {

            /* Allocate memory for the IOTable */

            vmac.cbsize = numSlots * sizeof(ulong_t);

            if ((rc = PSDHelp(router, PSDHLP_VMALLOC, &vmac)) == NO_ERROR) {

               IOTable = (void **)vmac.addr;

               /* Build IOTable, and IOConfigTable from infoTab */

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

                  /* Fill EBI2IOTable
                   *
                   * Depending on the state of infoTable[i].flags, either:
                   *
                   * Allocate a virtual address spanning the memory mapped
                   * I/O range for a given slot.
                   *
                   *  OR
                   *
                   * Allocate actual RAM of the size desired.
                   * infoTab[i].address is ignored in this scenrario.
                   */

                  if (infoTab[i].length) {

                     if (infoTab[i].flags & ALLOCATE_RAM) {

                        vmac.cbsize = infoTab[i].length;
                        vmac.flags = VMALLOC_FIXED;

                        if ((rc = PSDHelp(router, PSDHLP_VMALLOC, &vmac))
                           == NO_ERROR) {

                              IOTable[i] = (void *)vmac.addr;
                        }
                        else
                           break;

                     }
                     else {

                        vmac.addr = infoTab[i].address.low & ~PAGEMASK;
                        vmac.cbsize = (infoTab[i].address.low & PAGEMASK) +
                                      infoTab[i].length;
                        vmac.flags = VMALLOC_PHYS;

                        if ((rc = PSDHelp(router, PSDHLP_VMALLOC, &vmac))
                           == NO_ERROR) {

                              IOTable[i] = (void *)vmac.addr;
                        }
                        else
                           break;
                     }
                  }
               }

               if (rc == NO_ERROR) {

                  /* Initialize EBI function library */

                  if ((rc = ebi2CallTable.InitEBI(IOTable)) == NO_ERROR) {

                     /* Get number of physical processors */

                     if ((rc = ebi2CallTable.GetNumProcs(IOTable, &cPhysProcs))
                        == NO_ERROR) {

                        /* Get configuration of all processors */

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

                           if ((rc = ebi2CallTable.GetProcConf(IOTable,
                              i, &procConfig[i])) == NO_ERROR) {

                              /* Determine if processor usable */

                              if (  (procConfig[i].processorStatus !=
                                    PSTAT_ABSENT)
                                 && (procConfig[i].processorStatus !=
                                    PSTAT_FAULT)
                                 && (procConfig[i].processorType &
                                    PTYPE_80x86MASK)
                                 && (procConfig[i].boardType ==
                                    BTYPE_PROCESSOR)) {

                                    /* Increment usable processors */

                                    cUsableProcs++;
                                 }
                           }
                           else
                              break;
                        }

                        /* Set the inter processor interrupt vector */

                        rc = ebi2CallTable.SetIPIVector(IOTable,
                           -1, EBI_IPI_VECTOR);

                     }
                  }
               }
            }
         }
         /* Free infoTab */

         PSDHelp(router, PSDHLP_VMFREE, infoTab);
      }
   }

   if (rc == NO_ERROR) {

      ebi2CallTable.SetPanelProcGraphMode(IOTable, HISTO_MODE);

      /* Setup IPI IRQ info */

      irq.irq = EBI_IPI_IRQ;
      irq.flags = IRQf_IPI;
      irq.vector = EBI_IPI_VECTOR;
      irq.handler = 0;
      PSDHelp(router, PSDHLP_SET_IRQ, &irq);

      /* Fill init structure */

      pinit->flags = (INIT_GLOBAL_IRQ_ACCESS | INIT_USE_FPERR_TRAP);
      pinit->version = VERSION;
   }

   return (rc);
}


/***  ProcInit - Processor initalization
 *
 *    This function initalizes per processor items.
 *
 *    NOTE: This function is called once on each processor
 *          in the system.
 *
 *    ENTRY   None
 *
 *    EXIT    Return Code
 *
 */

ulong_t ProcInit(void) {

   ulong_t logicalProcID;
   ulong_t localIntMask;
   ulong_t rc;

   /* Get logical processor ID */

   if ((rc = ebi2CallTable.GetProcID(IOTable, &logicalProcID)) == NO_ERROR) {

      /* Cancel all requested interrupts */

      ebi2CallTable.CancelInterrupt(IOTable, -1, logicalProcID);

      /* Save the logical processor number */

      pPSDPLMA->procnum = logicalProcID;

      /* Set the IPI ID */

      if ((rc = ebi2CallTable.SetIPIID(IOTable, logicalProcID,
         1 << logicalProcID)) == NO_ERROR) {

         /* Set the local interrupt mask */

         if (pPSDPLMA->procnum)
            localIntMask = ~EBI_IPI_IRQBIT;
         else
            localIntMask = 0;

         if ((rc = ebi2CallTable.SetLocalIntMask(IOTable, localIntMask,
            logicalProcID)) == NO_ERROR) {

            /* Enable local cache */

            ebi2CallTable.EnableRAMCache(IOTable);
         }
      }
   }

   return (rc);
}


/***  StartProcessor - Start a processor
 *
 *    This function starts a processor.
 *
 *    ENTRY   procnum - processor number to start (0-based)
 *
 *    EXIT    Return Code
 *
 */

ulong_t StartProcessor(ulong_t procnum) {

   ulong_t rc;

   rc = ebi2CallTable.StartProc(IOTable, procnum);

   return (rc);
}


/***  GetNumOfProcs - Get number of processors
 *
 *    This function gets the number of processors which exist on this
 *    platform.
 *
 *    ENTRY   None
 *
 *    EXIT    Number of processors
 *
 */

ulong_t GetNumOfProcs(void) {

   ulong_t AllowableCPUs;

   AllowableCPUs = ( cUsableProcs > MAXCPUS ) ? MAXCPUS : cUsableProcs;

   return (AllowableCPUs);
}


/***  GenIPI - Generate an inter-processor interrupt
 *
 *    This function generates an IPI.
 *
 *    ENTRY   procnum - processor number to interrupt (0-based)
 *
 *    EXIT    NO_ERROR
 *
 */

ulong_t GenIPI(ulong_t procnum) {

   ulong_t rc;

   rc = ebi2CallTable.GenIPI(IOTable, 1 << procnum);

   return (rc);
}


/***  EndIPI - End an inter-processor interrupt
 *
 *    This function ends an IPI.
 *
 *    ENTRY   procnum - processor number to end interrupt on (0-based)
 *
 *    EXIT    Return Code
 *
 */

ulong_t EndIPI(ulong_t procnum) {

   ulong_t rc;

   rc = ebi2CallTable.MaskableIntEOI(IOTable, EBI_IPI_IRQ);

   return (rc);
}


/***  IrqMask - Mask/UnMask IRQ's
 *
 *    This function masks, and unmasks IRQ levels, when we are in
 *    advanced interrupt mode.
 *
 *    ENTRY   p - pointer to PSD_IRQ structure
 *
 *    EXIT    Return Code
 *
 */

ulong_t IrqMask(PSD_IRQ *p) {

   ulong_t rc = NO_ERROR;
   ulong_t flags;
   ulong_t mask;

   if (!AdvIntMode)
      return -1;

   flags = Disable();

   AcquireSpinLock();

   switch (p->flags) {

      case IRQ_MASK:
         /*
          * a bit set to 1 means disable the line
          */
         ebi2CallTable.GetGlobalIntMask(IOTable, &mask);
         mask |= p->data;
         ebi2CallTable.SetGlobalIntMask(IOTable, mask);
         break;

      case IRQ_UNMASK:
         /*
          * a bit set to 1 means enable the line
          */
         ebi2CallTable.GetGlobalIntMask(IOTable, &mask);
         mask &= ~p->data;
         ebi2CallTable.SetGlobalIntMask(IOTable, mask);
         break;

      case IRQ_GETMASK:
         /*
          * a bit set to 1 means the line is disabled,
          * a bit set to 0 means the line is enabled
          */
         ebi2CallTable.GetGlobalIntMask(IOTable, &mask);
         p->data = mask;
         break;

      case IRQ_NEWMASK:
         /*
          * a bit set to 1 means disable the line,
          * a bit set to 0 means enable the line,
          */
         ebi2CallTable.SetGlobalIntMask(IOTable, p->data);
         break;

      default:
         rc = -1;
         break;
   }

   ReleaseSpinLock();

   Enable(flags);

   return (rc);
}


/***  IrqReg - Return ISR/IRR
 *
 *    This function returns the ISR/IRR, when we are in
 *    advanced interrupt mode.
 *
 *    ENTRY   p - pointer to PSD_IRQ structure
 *
 *    EXIT    Return Code
 *
 */

ulong_t IrqReg(PSD_IRQ *p) {

   ulong_t rc = NO_ERROR;
   ulong_t isr, irr;
   ulong_t flags;

   if (!AdvIntMode)
      return -1;

   ebi2CallTable.GetGlobalIRQStatus(IOTable, &isr, &irr);

   switch (p->flags) {

      case IRQ_READ_IRR:
         p->data = irr;
         break;

      case IRQ_READ_ISR:
         p->data = isr;
         break;

      default:
         rc = -1;
   }

   return (rc);
}


/***  IrqEOI - Issue an EOI
 *
 *    This function issues an EOI, when we are in
 *    advanced interrupt mode.
 *
 *    ENTRY   p - pointer to PSD_IRQ structure
 *
 *    EXIT    Return Code
 *
 */

ulong_t IrqEOI(PSD_IRQ *p) {

   ulong_t rc;

   if (!AdvIntMode)
      return -1;

   rc = ebi2CallTable.MaskableIntEOI(IOTable, p->data);

   return (rc);
}


/***  SetAdvIntMode - Switch into advanced interrupt mode
 *
 *    This function switches into advanced interrupt mode.
 *
 *    ENTRY   None
 *
 *    EXIT    Return Code
 *
 */

ulong_t SetAdvIntMode(void) {

   ulong_t rc = NO_ERROR;
   ulong_t flags;
   ulong_t mask;
   int i;
   ulong_t vec[] = {0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
                    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77 };

   flags = Disable();

   if (!Bypass) {
      mask = (ulong_t)InByte(0xa1) << 8;
      mask |= (ulong_t)InByte(0x21);

      for (i = 0; i < 16; i++) {
         rc = ebi2CallTable.SetIRQVectorAssign(IOTable, i, vec[i]);
         if (rc != NO_ERROR)
            break;
      }

      if (rc == NO_ERROR) {

         ebi2CallTable.SetGlobalIntMask(IOTable, mask);

         rc = ebi2CallTable.SetAdvIntMode(IOTable);

         if (rc == NO_ERROR)
            AdvIntMode = 1;
      }
   }

   Enable(flags);

   return (rc);
}


/***  SetProcState - Log processor IDLE/BUSY
 *
 *    This function logs the calling processor as IDLE, or BUSY.
 *    The front panel will then be updated to reflect current
 *    processor utilization.
 *
 *    ENTRY   State - processor state
 *
 *    EXIT    Return Code
 *
 */

ulong_t SetProcState(ulong_t state) {

   ulong_t rc;

   if (state == PROC_STATE_IDLE)
      rc = ebi2CallTable.LogProcIdle(IOTable);
   else
      rc = ebi2CallTable.LogProcBusy(IOTable);

}


/***  ResetMode - Setup for system reset
 *
 *    This function sets the hardware into a mode for a system reset.
 *
 *    ENTRY   None
 *
 *    EXIT    Return Code
 *
 */

ulong_t ResetMode(void) {

   ulong_t rc = NO_ERROR;
   ulong_t i,irr,isr;
   ulong_t flags = Disable();       // disable interrupts

   for (i = 1; (rc == NO_ERROR) && (i < cUsableProcs); i++)
      rc = ebi2CallTable.StopProc(IOTable, i);

   /*
    * Need to EOI ant interrupts in the isr register.  They seem to hang the
    * system on a reboot when netbind.exe resets the token ring adapter
    * and ibmtokmp loops on a change in the system infoseg time, but for
    * some reason irq 8 is not comming in.  EOIing these interrupts
    * seems to allow irq 8 to come in.  Also added cli above.
    */

   ebi2CallTable.GetLocalIRQStatus(IOTable, 0, &isr, &irr);

   for (i = 0; i < 16 ; i++)
   {
     if (isr & 0x00000001)
     {
       ebi2CallTable.MaskableIntEOI(IOTable, i);      //eoi the keyboard interrupt
     }
     isr = isr >> 1;
   }

   ebi2CallTable.CancelInterrupt(IOTable, -1, 0); // cancel all interrupts

   if (rc == NO_ERROR)
      rc = ebi2CallTable.DeInitEBI(IOTable);

   return (rc);
}
