#include <dos.h>
#include "iwdefs.h"
#include "iwprotos.h"
extern IWAVE iw;
//########################################################################
// FILE: iwpnp.c
//
// REMARKS: This file contains the definitions for the InterWave's DDK
//          functions dedicated to the configuration of the InterWave
//          PNP logic.
//
// UPDATE: 4/07/95
//########################################################################
//
// FUNCTION: IwavePnpKey
//
// PROFILE: This function issues the initiation key that places the PNP
//          logic into configuration mode. The PNP logic is quiescent at
//          power up and must be enabled by software. This function will
//          do 32 I/O writes to the PIDXR (0x0279). The function will
//          first reset the LFSR to its initial value by a sequence of two
//          write cycles of 0x00 to PIDXR before issuing the key.
//
//########################################################################
void IwavePnpKey(void)
{
  BYTE code=0x6A;
  BYTE msb;
  BYTE i;

//###############################################
//  Reset Linear Feedback Shift Reg.
//###############################################
  _poke(0x279,0x00);
  _poke(0x279,0x00);

  _poke(0x279,code);          /* Initial value */

  for (i=1; i<32; i++)
  {
   msb = ((code&0x01)^((code&0x02)>>1))<<7;
   code = (code>>1)|msb;
   _poke(0x279,code);    
  }
}
//########################################################################
//
// FUNCTION: IwavePnpIsol
//
// PROFILE: This function will generate 72 pairs of I/O read cycles from
//          the PNPRDP register which is initially located at 0x203. The
//          function then checks for both 0x55 and 0xAA driven by the
//          hardware. If both 0x55 and 0xAA are read back, the function
//          assumes that the hardware had a "1" in that position. All
//          other results are assumed to be "0".
//
//          During the first 64 bits, the function generates a checksum
//          using the received data. The checksum is compared with the
//          checksum read back in the last 8 bits of the sequence.
//
//          if during the first iteration, the 0x55 and 0xAA is not
//          read back or the checksum does not match the function assumes
//          a PNPRDP conflict and relocates it. If after a few tries
//          the same situation arises, then the function assumes that
//          there is no PNP cards in the system.
//
//          This function will isolate each PNP card on the system by
//          assigning a unique CSN to each card. It will return the CSN
//          of the last card isolated (greatest CSN).
//
//          When this function exits all PNP cards are in the sleep
//          state.
//
//########################################################################
BYTE IwavePnpIsol(PORT *pnpread)
{
  BYTE checksum=0x6A;
  BYTE chksum=0x00;
  BYTE bit=0x00;
  PORT pnprdp=0x203;
  WORD data;
  BYTE csn=0x00;
  BYTE i;
  BYTE iteration=1;

//############################################################
//  The procedure to set the address of PNPRDP is :
//  1. Issue the Initiation Key.
//  2. Send Command Wake[0] to enter isolation.
//  3. Send command Set RD_DATA Port.
//############################################################

  IwavePnpKey();                    /* Issue Initiation Key */
  IwavePnpWake(0x00);               /* send command Wake[0] */
  _poke(0x279,0x00);                /* select PSRPAI to set addr of PNPRDP */
  _poke(0xA79,(BYTE)(pnprdp>>2));   /* set A[9:2] of PNPRDP address */
  _poke(0x279,0x01);                /* select PISOCI (Serial Isol Reg) */
  IwaveDelay(1);							/* wait 1 millisecond */

while(TRUE)
 {
  for (i=1; i<=64; i++)
	{                                 /* Vendor Id and Serial Number */
	 data=((WORD)_peek(pnprdp))<<8;   /* first read (0x55) */
    IwaveDelay(1);
	 data=data|_peek(pnprdp);         /* second read (0xAA) */
	 IwaveDelay(1);  						 /* wait 250useconds at least*/
	 if (data==0x55AA)
		  bit=0x01;
	 checksum=((((checksum^(checksum>>1))&0x01)^bit)<<7)|(checksum>>1);
	 bit=0x00;
	}
  for (i=65; i<=72; i++)
	{                                  /* checksum from card */
	 data=((WORD)_peek(pnprdp))<<8;    /* first read (0x55) */
    IwaveDelay(1);
	 data=data|(_peek(pnprdp)&0x00FF); /* second read (0xAA) */
    IwaveDelay(1);
	 if (data==0x55AA)
		  chksum=chksum|(1<<(i-65));
	}
  if ((checksum!=0x00)&&(checksum==chksum))
	 {
     csn++;
	  _poke(0x279,_PCSNI);            /* select PCSNI */
	  _poke(0xA79,csn);               /* set CSN */
     iteration++;
	  IwavePnpWake(0x00);             /* send command Wake[0] */
	  _poke(0x279,_PISOCI);           /* select PISOCI (Serial Isol Reg) */
    }

  if(((checksum==0x00)||(chksum!=checksum))&&(iteration==1))
    {
      if (pnprdp>0x23F)
			 return(PNP_ABSENT);         /* No PNP cards in system */
        
		pnprdp+=0x04;                   /* Next PNPRDP to try */
      checksum=0x6A;
      chksum=0x00;
      IwavePnpWake(0x00);               /* send command Wake[0] */
		_poke(0x279,0x00);                /* set new addr of PNPRDP */
		_poke(0xA79,(BYTE)(pnprdp>>2));   /* set A[9:2] of PNPRDP address */
		_poke(0x279,_PISOCI);             /* select PISOCI (Serial Isol Reg) */
    }
 
  if(((checksum==0x00)||(chksum!=checksum))&&(iteration>1))  
     break;

  checksum=0x6A;                     /* reset for next card */
  chksum = 0x00;                     /* reset for next card */
  bit=0x00;
 }
 *pnpread=pnprdp;
 return(csn);
}
//########################################################################
//
// FUNCTION: IwavePnpSerial
//
// PROFILE: This function reads the first nine bytes of the data from
//          the serial EEPROM and returns the Vendor ID and the serial
//          number. First, it resets the EEPROM control logic by
//          issuing a WAKE[CSN] command. The function will return an
//          ASCII string for the vendor ID into the char array pointed
//          to by "vendor" in the VVVNNNN format. The serial number
//          is placed in the 32-bit variable pointed to by "serial".
//          Note that the 9th byte is read but not used as it is invalid
//          when the serial identifier is read via PRESDI.
//
//          This function assumes that the PNP state machine is not in
//          the "wait for key state". Otherwise, unpredictable results
//          will be obtained.
//
//########################################################################
void
IwavePnpSerial(PORT pnprdp,
               BYTE csn,
               BYTE  *vendor,
               DWORD *serial)
{
 BYTE presdi, digit, i;

 *serial=0L;

//#######################################
// Reset Serial EEPROM logic
//#######################################
 IwavePnpWake(csn);             /* Wake card up */

 for (i=1; i<=4; i++)
 {
   IwavePnpPeek(pnprdp,1,&presdi);
 
 switch(i)
  {
   case 1:
   *(vendor++)=((presdi&0x7C)>>2)|0x40; /* 1st char */
   *vendor=(presdi&0x03)<<3;            /* isolate bits[4:3] of 2nd char */
   break;
   case 2:
   *vendor=((presdi&0xE0)>>5)|(*vendor);
   *(vendor++)=(*vendor)|0x40;               /* 2nd char */
   *vendor=(presdi&0x1F)|0x40;               /* 3rd char */
   break;
   case 3:
   case 4:
   digit=(presdi&0xF0)>>4;
   if (digit<=0x09)
       *(++vendor)=digit+0x30;               /* ASCII of digit */
   else
       *(++vendor)=(digit&0x07)+0x3F;
   digit=presdi&0x0F;
   if (digit<=0x09)
       *(++vendor)=digit+0x30;
   else
       *(++vendor)=(digit&0x07) + 0x3F;
   break;
  }
 }
  *(++vendor)='\0';
  IwavePnpPeek(pnprdp,4,(BYTE *)serial);
  IwavePnpPeek(pnprdp,1,NULL);            /* discard checksum */
}
//########################################################################
//
// FUNCTION: IwavePnpPeek
//
// PROFILE: This function will return the number of specified bytes of
//          resource data from the serial EEPROM. The function will NOT
//          reset the serial EEPROM logic to allow reading the entire
//          EEPROM by issuing repeated calls. The caller must supply a
//          pointer to where the data are to be stored.
//          It is assumed that the InterWave is not in either "sleep"
//          or "wait for key" states. Note that on the first call, if
//          the caller means to read from the beggining of data the
//          serial EEPROM logic must be reset. For this, the caller
//          should issue a WAKE[CSN] command
//
//########################################################################
void IwavePnpPeek(PORT pnprdp, WORD bytes, BYTE *data)
{
 WORD i;
 BYTE datum;

 for (i=1; i<=bytes; i++)
 {
	_poke(_PIDXR,0x05);          /* select PRESSI */

	while(TRUE)                  /* wait til new data byte is ready */
	{
	 if(_peek(pnprdp)&PNP_DATA_RDY)
		 break;                   /* new resource byte ready */
	}
	_poke(_PIDXR,0x04);          /* select PRESDI */
	datum=_peek(pnprdp);         /* read resource byte */
	if(data!=NULL)
	*(data++)=datum;             /* store it */
 }
}
//########################################################################
//
// FUNCTION: IwavePnpEeprom
//
// PROFILE: This function allows the caller to control the serial
//          EEPROM directly while the audio device is inactive. To
//          de-activate the audio device issue the call
//          IwavePnpActivate(AUDIO,OFF).
//
//########################################################################
void IwavePnpEeprom(BYTE ctrl)
{
    ENTER_CRITICAL;
    _poke(_PIDXR,0xF1);       /* select PSECI */
    _poke(_PNPWRP,ctrl);      /* write PSECI */
    LEAVE_CRITICAL;
}
//########################################################################
//
// FUNCTION: IwavePnpActivate
//
// PROFILE: This function will activate or de-activate the audio device
//          or the external device on the InterWave. Set the "dev" arg
//          to AUDIO for the audio device or EXT for the external device.
//          Set "bool" to ON or OFF to turn the device on or off the ISA
//          bus. Notice that for a logical device to work, it must be
//          activated.
//
//########################################################################
void IwavePnpActivate(BYTE dev, BYTE bool)
{
	 IwavePnpDevice(dev);           /* select audio device */
	 ENTER_CRITICAL;
	 _poke(_PIDXR,ACTIVATE_DEV);    /* select Activate Register */
	 _poke(_PNPWRP,bool);           /* write register */
	 LEAVE_CRITICAL;

}
//########################################################################
//
// FUNCTION: IwavePnpDevice
//
// PROFILE: This function allows the caller to select between five
//          logical devices available on the InterWave.It is assumed
//          that the PNP state machine is in configuration mode.
//
//########################################################################
void IwavePnpDevice(BYTE dev)
{
    ENTER_CRITICAL;
    _poke(_PIDXR,_PLDNI);            /* select PLDNI */
    _poke(_PNPWRP,dev);              /* write PLDNI */
    LEAVE_CRITICAL;
}
//########################################################################
//
// FUNCTION: IwavePnpPower
//
// PROFILE: This function allows the caller to disable major sections of
//          the InterWave to prevent them from consuming power and
//          loading the ISA bus.
//
//          It is assumed that the PNP state machine is in configuration
//          mode.
//
//########################################################################
void IwavePnpPower(BYTE mode)
{
  ENTER_CRITICAL;
  _poke(_PIDXR,_PPWRI);    /* select PPWRI */
  _poke(_PNPWRP,mode);     /* write PPWRI */
  LEAVE_CRITICAL;
}
//########################################################################
//
// FUNCTION: IwavePnpWake
//
// PROFILE: This function issues a WAKE[CSN] command to the InterWave. If
//          the CSN matches the PNP state machine will enter the
//          configuration state. Otherwise it will enter the sleep mode.
//
//          It is assumed that the PNP state machine is not in the
//          "wait for key" state.
//
//########################################################################
void IwavePnpWake(BYTE csn)
{
  ENTER_CRITICAL;
  _poke(_PIDXR,_PWAKEI);  /* select PWAKEI */
  _poke(_PNPWRP,csn);     /* write csn */
  LEAVE_CRITICAL;
}
//########################################################################
//
// FUNCTION: IwavePnpIOcheck
//
// PROFILE: This function allows the caller to perform a conflict check
//          on an I/O port to be used by a logical device. The function
//          receives the base address of the I/O range as well as the
//          number of ports in the range and then performs the I/O check
//          protocol. It returns the address of the port if a conflict
//          is detected or IO_CHK if no conflict is detected.
//
//          This function assumes that the logical device has been de-
//          activated and that the PNP state machine is in config mode.
//
//########################################################################
PORT IwavePnpIOcheck(PORT base, BYTE no_ports)
{
  BYTE i;
  PORT portid;

  _poke(_PIDXR,RANGE_IOCHK);          /* select IO range check reg */

  for (i=0; i<no_ports; i++)
  {
	portid=base+i;                     /* port to check */
	_poke(_PNPWRP,0x02);               /* must drive 0xAA onto bus */
	if (_peek(portid)!=0xAA)
		 return(portid);                /* IO conflict detected */

	_poke(_PNPWRP,0x03);               /* must drive 0x55 onto bus */
	if (_peek(portid)!=0x55)
		 return(portid);                /* IO conflict detected */
	}
	return(IO_OK);
}
//########################################################################
//
// FUNCTION: IwavePnpGetCSN
//
// PROFILE: This function allows the caller to detect an InterWave based
//          adapter board and will return its asigned CSN so that an
//          an application can access its PnP interface and determine the
//          borad's current configuration. In conducting its search for
//          the InterWave IC, the function will use the first 32 bits of
//          the Serial Identifier called the vendor ID in the PnP ISA
//          spec. The last 4 bits in the Vendor ID represent a revision
//          number for the particular product and this function gives the
//          caller the option of taking this revision number into account
//          or not in the search. If the function fails to find the
//          InterWave IC it will return FALSE.
//
//########################################################################
BYTE IwavePnpGetCSN(DWORD VendorID, BYTE csn_max)
{
	 BYTE csn;
	 DWORD vendor;

	 IwavePnpKey();               /* Key to access PnP Interface */
	 VendorID&=(0xFFFFFFF0);      /* reset 4 least significant bits */

	 for(csn=1;csn<=csn_max;csn++)
	 {
		 IwavePnpWake(csn);                         /* Select card */
		 IwavePnpPeek(iw.pnprdp,4,(BYTE *)&vendor); /* get vendor ID */
       vendor&=(0xFFFFFFF0);
		 if (vendor==VendorID)     /* If IDs match, InterWave is found */
			 {
			  _poke(_PIDXR,0x02);   /* Place all cards in wait-for-key state */
			  _poke(0x0A79,0x02);
			  return(csn);
			 }
	 }
	 _poke(_PIDXR,0x02);          /* Place all cards in wait-for-key state */
	 _poke(0x0A79,0x02);
	return(FALSE);                /* InterWave IC not found */
}
//########################################################################
//
// FUNCTION: IwavePnpPing
//
// PROFILE: This function allows the caller to detect an InterWave based
//          adapter board and will return its asigned CSN so that an
//          an application can access its PnP interface and determine the
//          borad's current configuration. In conducting its search for
//          the InterWave IC, the function will use the first 32 bits of
//          the Serial Identifier called the vendor ID in the PnP ISA
//          spec. The last 4 bits in the Vendor ID represent a revision
//          number for the particular product and will not be included
//          in the search. The function will return the Vendor ID and the
//          calling application should check the revision bits to make
//          sure they are compatible with the board.
//
//########################################################################
BYTE IwavePnpPing(DWORD VendorID)
{
  BYTE csn;

  VendorID&=(0xFFFFFFF0);      /* reset 4 least significant bits */
  IwavePnpKey();               /* Key to access PnP Interface */
  while (iw.pnprdp<=0x23F)
  {
	 for(csn=1;csn<=10;csn++)
	 {
		 IwavePnpWake(csn);                            /* Select card */
		 IwavePnpPeek(iw.pnprdp,4,(BYTE *)&iw.vendor); /* get vendor ID */
		 if (((iw.vendor)&0xFFFFFFF0)==VendorID)       /* If IDs match, InterWave is found */
			 {
			  _poke(_PIDXR,0x02);   /* Place all cards in wait-for-key state */
			  _poke(0x0A79,0x02);
			  return(csn);
			 }
	 }
   iw.pnprdp+=0x04;
  }
  _poke(_PIDXR,0x02);            /* Place all cards in wait-for-key state */
  _poke(0x0A79,0x02);
  return(FALSE);                 /* InterWave IC not found */
}
#ifndef PROTECTED_MODE
//########################################################################
//
// FUNCTION: IwavePnpBIOS
//
// PROFILE: This function allows the caller to detect the presence of a
//          Plug-and-Play BIOS on a PC. The function checks that the PnP
//          BIOS structure is valid and returns its address. This function
//          may be useful to programs that need to access PnP BIOS
//          services. Not available to protected mode programs.
//
//########################################################################
BYTE far *IwavePnpBIOS(void)
{
	void far *ptrS;
	BYTE far *byteS;
	BYTE chksum=0;
	DWORD signature=0L;
	WORD i, length;

	ptrS=MK_FP(0xF000,0);
   byteS=(BYTE far *)ptrS;

    for (i=1; i<=4094; i++)
    {
    signature=*byteS++;
    signature=(signature<<8)|*byteS++;
    signature=(signature<<8)|*byteS++;
    signature=(signature<<8)|*byteS;
    byteS-=3;

    if (signature==0x24506E50)
        break;
    else
        byteS=byteS+0x10;
   }
    if (signature==0x24506E50)
    {
		  length=*(byteS+5);
		  for (i=0;i<length; i++)
				 chksum=chksum+*(byteS+i);
	 if (chksum==0)
		  return(byteS);
	 }
    return((BYTE far *)NULL);
}
//########################################################################
//
// FUNCTION: IwavePnpBIOS40
//
// PROFILE: This function allows the caller to determine the total number
//          of PnP Cards in the system as well as the location of the
//          PnP READ DATA PORT. Note that this is a real-mode function and
//          should not be called from a protected mode program. It uses
//          PnP BIOS service function 40 to fill a structure with the
//          following information:
//          1. Structure Revision (BYTE)
//          2. Total Number of PnP Cards (BYTE) and
//          3. PnP Read Data Port (PORT)
//
//          The function takes two args:
//
//          ptrS - Far pointer to installation check struture
//          cfig - structure with the three item mentioned above in that
//                 order.
//
//          Note that this function returns 0 if successful or !0 if
//          it fails.
//
//########################################################################
WORD IwavePnpBIOS40(BYTE far *ptrS, BYTE far *cfig)
{
	WORD code;
	WORD BiosSelector;
	int (far *func)();

	func=(int (far*)())MK_FP(*((WORD far *)(ptrS+0x0F)),*((WORD far *)(ptrS+0x0D)));
	BiosSelector=*((WORD far*)(ptrS+0x1B));
	return(func(0x40,cfig,BiosSelector));
}
#endif

