/* **********************************
 * *                                *  
 * *           DSP Driver           *
 * *                                *  
 * **********************************  
 * File: libs/dsp/dspr_subs.c
 *     
 *  COPYRIGHT (c) 1987, 1988, 1989, 1993, 1997, 1999, 2000, 2001, 2002,
 *                                2003 BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.  
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED. 
 */     
#include <string.h>
#include <sys/time.h>

#define INCLUDE_SYSTIME_PROTOTYPES 1
#include "sigtypes.h"
#include "dsp.h"
#include "rvp8.h"
#include "dsp_lib.h"
#include "user_lib.h"

#include "dsp_def.h"

static void encrypt_license 
( unsigned int licseed_a[2],
  unsigned int licheck_a[2] ) ;

/* ======================================================================
 * Get the GPARM parameters from the signal processor.  Optional bits
 * control whether the latched status bits are cleared or kept after
 * the data are fetched.
 */ 
MESSAGE dspr_gparm
( Dsp *dsp_a, struct gparm *pGparm_a, UINT4 iOptions_a ) 
{   
  MESSAGE istatus;
  SINT4 iactcnt;

  icmd_c[0] = OP_GPARM;
  if (iOptions_a&GPARM_LSTATUS_KEEP) icmd_c[0] |= 0x0020;
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  if (istatus != SS_NORMAL) return istatus;

  istatus = dsp_read( dsp_a, &gparm_c, 64, &iactcnt );

  if( ( pGparm_a != NULL) && (pGparm_a != &gparm_c) )
    memmove( pGparm_a, &gparm_c, sizeof(*pGparm_a) );
  
  return istatus;
}

/* ======================================================================
 * Extended processor parameters can also be read back and converted
 * to convenient native form.
 */
MESSAGE dspr_exParm( Dsp *dsp_a, struct dspExParm *pParm_a )
{
  MESSAGE istatus = DSP_FEATURE_MISSING ;
  memset( pParm_a, 0, sizeof(struct dspExParm) ) ;

  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    struct dspExParmIO parmIO ;

    istatus = dspr_rback( dsp_a, RBACK_EXPARM, (void *)&parmIO,
			  sizeof(parmIO)/sizeof(UINT2) ) ;

    /* If we've successfully read the IO form of the structure, then
     * convert to the native representation.  The driver does this so
     * that these conversions do not sprawl out into user code.
     */
    if( istatus == SS_NORMAL ) {
      SINT4 ii, ij ; char *pCh ;
      /* Decode the site name
       */
      pCh = pParm_a->sSiteName ;
      for( ii=0 ; ii < DSPSITENAMELEN/2 ; ii++ ) {
	ij = parmIO.iSiteName[ ii ] ;
	*pCh++ = (ij & 0x7F) ; *pCh++ = ((ij >> 8) & 0x7F) ;
      }
      /* Convert some split numerical fields to floating point
       */
      pParm_a->fMaxRangeKM =
	((FLT4)parmIO.iMaxRangeKM) + (1E-3 * (FLT4)parmIO.iMaxRangeM) ;

      pParm_a->fPulseWidthUSec =
	((FLT4)parmIO.iPwUSec    ) + (1E-3 * (FLT4)parmIO.iPwNSec   ) ;

      pParm_a->fRayNomPRF =
	((FLT4)parmIO.iRayHiNomPRF) + (((FLT4)parmIO.iRayLoNomPRF) / 65536.0) ;

      /* Convert UTC time of last processed ray to more convenient
       * YMDS structure format.
       */
      YmdsFromTime_t( &pParm_a->rayUTC,
		      (((UINT4)parmIO.iRayHiUTC) << 16) + (UINT4)parmIO.iRayLoUTC,
		      parmIO.iRayMsUTC, TIME_FLG_UTC ) ;

      /* Copy simple fields across...
       */
      pParm_a->iBurstPhaseLock = parmIO.iBurstPhaseLock ;
      pParm_a->lBurstAmplCor   = parmIO.lBurstAmplCor ;

      pParm_a->iMismatchTS     = parmIO.iMismatchTS ;
      pParm_a->iRayMajorMode   = parmIO.iRayMajorMode ;
      pParm_a->iRayBinCount    = parmIO.iRayBinCount ;
      pParm_a->iRayUmode       = parmIO.iRayUmode ;
      pParm_a->iRayPWidth      = parmIO.iRayPWidth ;
      pParm_a->iRayPhaseMod    = parmIO.iRayPhaseMod ;
      pParm_a->iRayPolar       = parmIO.iRayPolar ;

      pParm_a->iNoiseAZ        = parmIO.iNoiseAZ ;
      pParm_a->iNoiseEL        = parmIO.iNoiseEL ;

      pParm_a->fBandWidthMHz   = 1E-3 * (FLT4)parmIO.iBandWidthKHz ;
      pParm_a->fBurstFreqMHz   = 2E-3 * (FLT4)parmIO.iBurstFreq2KHz ;
      pParm_a->fBurstPowerDBm  = 1E-2 * (FLT4)parmIO.iBurstPowerDBm100 ;

      for( ii=0 ; ii < 2 ; ii++ ) {
	pParm_a->iIFDTempCaseDegC[ii] = parmIO.iIFDTempCaseDegC[ii] ;
	pParm_a->iIFDTempFPGADegC[ii] = parmIO.iIFDTempFPGADegC[ii] ;
      }
    }
  }
  return( istatus ) ;
}

/* ======================================================================
 * This routine is called to get Read-Back data from the RVP6.  It
 * reads ICOUNT_A words containing data selected by IOP_A into the
 * PDATA_A output array.
 */ 
MESSAGE dspr_rback
( Dsp *dsp_a, UINT2 iop_a, void *pData_a,  UINT2 icount_a ) 
{   
  MESSAGE istatus ; SINT4 iactcnt ;
      
  icmd_c[0] = OP_RBACK + ((iop_a & 0x0FF) << 8) ;
  icmd_c[1] = icount_a ;
  istatus = dsp_write( dsp_a, icmd_c, 2 );
  if( istatus != SS_NORMAL ) return istatus;

  istatus = dsp_read( dsp_a, pData_a, icount_a, &iactcnt );
  
  return istatus;
}

/* ======================================================================
 * Return the current value of the user specified bits for IQ headers.
 */ 
MESSAGE dspr_uiqBits( Dsp *dsp_a, struct uiqbits *uiqbits_a )
{
  MESSAGE istatus =
    dspr_rback( dsp_a, RBACK_UIQBITS, (UINT2 *)uiqbits_a,
		sizeof(struct uiqbits) / sizeof(UINT2) ) ;
  return( istatus ) ;
}

/* ======================================================================
 * Return bits set for each of various options and formats that this DSP
 * supports.
 *
 * Bits set in itsformats_a: Selections from TSOUT_xxx
 *    Bits set in ipmodes_a: Selections from PMODE_xxx
 */
MESSAGE dspr_formats
( Dsp *dsp_a,			/* Dsp structure */
  UINT4 *itsformats_a,		/* Time series formats returned here */
  UINT4 *ipmodes_a)		/* Processing mode options returned here */
{
  MESSAGE istatus = SS_NORMAL;

  *itsformats_a = BIT( TSOUT_TS8 ) ; /* They all support 8-bit time series */
  *ipmodes_a    = BIT( PMODE_PPP ) ; /* They all support PPP mode */

  /* Make sure internal GPARM information is valid.
   */
  istatus = dspr_pram_internal( dsp_a ) ;
  if( istatus != SS_NORMAL ) return( istatus ) ;
  
  /* Check bits for 16-bit TS, and for power spectra
   */
  if( gparm_c.istat_i  & GIS_16BITTS      ) *itsformats_a |= BIT( TSOUT_TS16  ) ;
  if( gparm_c.istat_i  & GIS_SPEC         ) *itsformats_a |= BIT( TSOUT_SPEC  ) ;
  if( gparm_c.istat_i4 & GI4S_IQPACKHISNR ) *itsformats_a |= BIT( TSOUT_HISNR ) ;
  
  /* Check bits for processing mode options
   */
  if( gparm_c.istat_i2 & GI2S_FFT   ) *ipmodes_a |= BIT( PMODE_FFT  ) ;
  if( gparm_c.istat_i2 & GI2S_RPH   ) *ipmodes_a |= BIT( PMODE_RPH  ) ;
  if( gparm_c.istat_i2 & GI2S_KNM   ) *ipmodes_a |= BIT( PMODE_KNMI ) ;
  if( gparm_c.istat_i2 & GI2S_DPT1  ) *ipmodes_a |= BIT( PMODE_DPT1 ) ;

  if( gparm_c.istat_i3 & GI3S_DPT2  ) *ipmodes_a |= BIT( PMODE_DPT2  ) ;
  if( gparm_c.istat_i3 & GI3S_USER1 ) *ipmodes_a |= BIT( PMODE_USER1 ) ;
  if( gparm_c.istat_i3 & GI3S_USER2 ) *ipmodes_a |= BIT( PMODE_USER2 ) ;
  if( gparm_c.istat_i3 & GI3S_USER3 ) *ipmodes_a |= BIT( PMODE_USER3 ) ;
  if( gparm_c.istat_i3 & GI3S_USER4 ) *ipmodes_a |= BIT( PMODE_USER4 ) ;

  if( gparm_c.istat_i4 & GI4S_BATCH ) *ipmodes_a |= BIT( PMODE_BATCH ) ;

  return( istatus ) ;
}

/* ======================================================================
 * This routine is called to read the walking one pattern from one of
 * the DSP.
 */
MESSAGE dspr_walk( Dsp *dsp_a, UINT2 iwalk_a[16] )
{
  MESSAGE istatus; SINT4 iactcnt;
      
  icmd_c[0] = OP_OTEST;
  istatus = dsp_write( dsp_a, icmd_c, 1 );
  if (istatus!=SS_NORMAL) return istatus;

  istatus = dsp_read( dsp_a, iwalk_a, 16, &iactcnt );
      
  return istatus;
}

/* ======================================================================
 * Routine to return a bitmask of spectra types that can be
 * viewed from this DSP.
 */
MESSAGE dspr_viewspecs
( Dsp *dsp_a,
  UINT4 *iViewspecMask_a )
{
  MESSAGE istatus = SS_NORMAL ; UINT4 iMask = SPEC_DEFAULT_BITS ;

  if( ( dsp_a->itype == DSP_TYPE_RVP8 ) ||
      ( ( dsp_a->itype == DSP_TYPE_RVP7                         ) &&
	( SS_NORMAL    == (istatus = dspr_pram_internal(dsp_a)) ) &&
	( dsp_cmp_version( &gparm_c, 23, 2 ) >= 0               ) ) ) {

    iMask |= ( SPEC_BIT( SPEC_RAWRES1 ) | SPEC_BIT( SPEC_RAWRES2 ) ) ;
  }
  *iViewspecMask_a = iMask ;
  return( istatus ) ;
}

/* ======================================================================
 * Return the complete array of clutter filter definition structures
 * that are currently being used by the DSP.  The all-pass filter in
 * slot #0 is always returned as all zeros.
 */
MESSAGE dspr_filterSpecs
( Dsp *dsp_a, struct rvp8SpecFilt pFilt_a[ NFILTS ] )
{
  MESSAGE istatus = DSP_FEATURE_MISSING ;
  memset( pFilt_a, 0, NFILTS * sizeof(struct rvp8SpecFilt) ) ;

  if( dsp_a->itype >= DSP_TYPE_RVP8 ) {
    struct rvp8SpecFiltIO specIO[ NFILTS-1 ] ;
    SINT4 iSlot, nWords = sizeof(specIO)/sizeof(UINT2) ;

    istatus = dspr_rback( dsp_a, RBACK_LFSPECS, specIO, nWords ) ;

    if( istatus == SS_NORMAL ) {
      for( iSlot=0 ; iSlot < (NFILTS-1) ; iSlot++ ) {
	rvp8DecodeSpecFilt( &pFilt_a[ iSlot+1 ], &specIO[ iSlot ] ) ;
      }
    }
  }
  return( istatus ) ;
}

/* ======================================================================
 * This routine is called to read the currently requested pulse width, 
 * and PRF.
 */
MESSAGE dspr_prf
( Dsp *dsp_a,
  FLT4 *fPrf_a,      /* Current desired PRF */
  UINT2 *ipwcode_a ) /* Current desired PW code */
{      
  MESSAGE istatus;

  /* Set default return values for error cases
   */
  *fPrf_a    = 1.0 ;
  *ipwcode_a = 0 ;

  istatus = dspr_pram_internal( dsp_a );
  if( istatus != SS_NORMAL ) return istatus;

  /* Pick out the pulse width and waveforms
   */
  *ipwcode_a = 3 &  (gparm_c.ipw_now) ;

  *fPrf_a = fDspPrfFromPrtIn( dsp_a, gparm_c.iprt_des );
      
  return istatus;
}

/* ======================================================================
 * Read the next word or group of words from the DSP on the assumption
 * that it is in chat mode.  Return a structure indicating the type of
 * data that was found.
 */
static MESSAGE ttyop_read
( Dsp *dsp_a, UINT2 *irbuf_a, SINT4 ircnt_a, SINT4 *iactwcnt_a ) ;

MESSAGE dspr_ttyop
( Dsp *dsp_a,
  struct ttyop_readback *ttyop_a )
{
  MESSAGE istatus = SS_NORMAL ; UINT2 *uptr ; SINT4 ii, ij ;

  /* If we don't have any previously data, then check whether there
   * are any new words available for reading.
   */
  if( iTTYCount_c == 0 ) {
    istatus = DspReadAvail( dsp_a, iTTYBuf_c, TTYREADAHEAD, 1, &iTTYCount_c ) ;
    iTTYPtr_c = 0 ;
  }
  if( istatus == SS_NORMAL ) {
    if( iTTYCount_c ) {
      UINT2 iword ; SINT4 icnt ;
      /* We can read something. Get one word and check the type
       * based on the high bits of the word.
       */
      istatus = ttyop_read( dsp_a, &iword, 1, &icnt ) ;
      if( (istatus == SS_NORMAL) && (icnt == 1) ) {
	switch( 0xFF00 & iword ) {
	case 0x0000:		/* Normal TTY character */
	  if( iword & 0x0080 ) {
	    ttyop_a->itype = TTYOP_ERROR ;
	  } else {
	    ttyop_a->u.ittychar = (iword & 0xFF) ;
	    ttyop_a->itype = TTYOP_TTYCHAR ;	  
	  }
	  break ;

	case 0x8000:		/* Scope plotting On/Off */
	  ttyop_a->u.lscope_active = (iword & 1) ;
	  ttyop_a->itype = TTYOP_SCOPE_ACTIVE ;	  
	  break ;

	case 0x9000:		/* Scope stroke intensities */
	  for( ii=0 ; ; ii++ ) {
	    struct scope_line_params *slp = &ttyop_a->u.scope_config.scope_line[ii*4] ;
	    for( ij=0 ; ij < 4 ; ij++ ) {
	      slp[ij].intensity = (iword & 0x3) ;
	      slp[ij].lvisible  = (slp[ij].intensity != 0) ;
	      iword >>= 2 ;
	    }
	    if( ii > 2 ) break ;
	    istatus = ttyop_read( dsp_a, &iword, 1, &icnt ) ;	  
	    if( istatus != SS_NORMAL ) break ;
	  }
	  ttyop_a->itype = TTYOP_SCOPE_CONFIG ;
	  break ;

	case 0xA000:		/* Scope stroke data */
	  uptr = ttyop_a->u.scope_data.idata ;
	  istatus = ttyop_read( dsp_a, uptr, PLOTLEN, &icnt ) ;
	  if( istatus != SS_NORMAL ) break ;
	  for( ii=0 ; ii < PLOTLEN ; ii++ ) uptr[ii] &= 0x0FFF ;
	  ttyop_a->u.scope_data.iplotnum = (iword & 0xF) ;
	  ttyop_a->itype = TTYOP_SCOPE_DATA ;	  
	  break ;

	default:		/* Unrecognized contents */
	  ttyop_a->itype = TTYOP_ERROR ;
	  break ;
	}
      }
    } else {
      /* If we can't read anything, then there is nothing in the
       * structure, but there also is no error.
       */
      ttyop_a->itype = TTYOP_NONE ;
    }
  }
  return( istatus ) ;
}

/* Internal TTYOP routine that is much like dsp_read(), except that it
 * first supplies data from the local read-ahead buffer whenever
 * possible.
 */
static MESSAGE ttyop_read
( Dsp *dsp_a, UINT2 *irbuf_a, SINT4 ircnt_a, SINT4 *iactwcnt_a ) 
{
  MESSAGE istatus = SS_NORMAL ; SINT4 iactwcnt = 0, iRemain = ircnt_a ;
  SINT4 iFetch = MIN( iRemain, iTTYCount_c ), iMore ;

  if( iFetch > 0 ) {
    memcpy( irbuf_a, &iTTYBuf_c[ iTTYPtr_c ], 2*iFetch ) ;
    iRemain     -= iFetch ; iactwcnt  += iFetch ; 
    iTTYCount_c -= iFetch ; iTTYPtr_c += iFetch ;
  }
  /* Fix possible alignment problems by reading a single short into a
   * local aligned buffer.
   */
  if( (iRemain > 0) && (0x2 & (UINT4)&irbuf_a[ iFetch ]) ) {
    UINT2 iBuf[1] ;
    istatus = dsp_read( dsp_a, iBuf, 1, &iMore ) ;
    if( istatus == SS_NORMAL ) {
      irbuf_a[ iFetch++ ] = iBuf[0] ; iRemain-- ; iactwcnt++ ;
    }
  }
  /* Read the rest, if any, and if no problems so far.
   */
  if( (iRemain > 0) & (istatus == SS_NORMAL) ) {
    istatus = dsp_read( dsp_a, &irbuf_a[ iFetch ], iRemain, &iMore ) ;
    iactwcnt += iMore ;
  }
  *iactwcnt_a = iactwcnt ;
  return( istatus ) ;
}

/* ********************************
 * *                              *
 * * License and Version Routines *
 * *                              *
 * ********************************
 */

/* ======================================================================
 * Internal routine to read the operational parameters into the
 * internal shared storage area.  This routine also switches on RVP6
 * and 7 features that are licensed for use only on IRIS systems.
 */
MESSAGE dspr_pram_internal
  (Dsp *dsp_a )
{
  MESSAGE istatus = SS_NORMAL;
  UINT2 lhas_features;

  /* First check that we have not done this since the DSP was
   * originally opened.  We do not want to have all of this overhead
   * every time.
   */
  if( lgparm_valid_c ) return SS_NORMAL;

  /* Get preliminary parameters from the signal processor.  If this is
   * not an RVP6, or if it is an early version of the RVP6, then we
   * are done.
   */
  istatus = dspr_gparm( dsp_a, NULL, GPARM_LSTATUS_KEEP ) ;
  if( istatus != SS_NORMAL ) return istatus;

  lhas_features=FALSE;
  switch( dsp_a->itype)
    {
    case DSP_TYPE_RVP6:
    case DSP_TYPE_RVP6_2:
      if( DSP_VERSION( &gparm_c ) >= 26 ) lhas_features=TRUE;
      break;
    case DSP_TYPE_RVP7:
    case DSP_TYPE_RVP8:
      lhas_features=TRUE;
      break;
    }
	
  if (lhas_features)
    {
      /* If there are features to license, then send down the license
       * opcode, read back the 4-word magic number, encrypt it and
       * write back a 4-word validation code.
       */
      icmd_c[0] = XOPC(XOP_LICENSE) ; istatus = dsp_write( dsp_a, icmd_c, 1 ) ;
      if( istatus == SS_NORMAL ) {
	UINT2 imagic[4] ; SINT4 iactcount ;

	sig_microSleep(50000) ; istatus = dsp_read( dsp_a, imagic, 4, &iactcount ) ;

	if( istatus == SS_NORMAL ) {
	  unsigned int licseed[2], licheck[2] ;

	  licseed[0] = imagic[0] + (imagic[1] << 16) ;
	  licseed[1] = imagic[2] + (imagic[3] << 16) ;
	  encrypt_license( licseed, licheck ) ;
	  icmd_c[0] = licheck[0] ;
	  icmd_c[1] = licheck[0] >> 16 ;
	  icmd_c[2] = licheck[1] ;
	  icmd_c[3] = licheck[1] >> 16 ;
	  icmd_c[4] = 1 ;
	  icmd_c[5] = 0 ;
	  icmd_c[6] = 0 ;
	  icmd_c[7] = 0 ;
	  istatus = dsp_write( dsp_a, icmd_c, 8 ) ;
	  
	  /* If that all worked properly, then read the parameters
	   * back one more time, since the licensing may have altered
	   * some of the values.
	   */
	  if( istatus == SS_NORMAL ) istatus = dspr_gparm( dsp_a, NULL, GPARM_LSTATUS_KEEP ) ;
	}
      }
    }
  if( istatus == SS_NORMAL ) lgparm_valid_c = TRUE ;
  return( istatus ) ;
}

/* ======================================================================
 * Given 64-bits of license seed, compute a 64-bit trapdoor check field
 * that is difficult to discover by trial and error.  This same code
 * appears in the RVP6 and in the IRIS DSP driver.
 */
static void encrypt_license
( unsigned int licseed_a[2],
  unsigned int licheck_a[2] )
{
  unsigned int ipoly=0x19E405DB, ibit ;
  int ii, ij, ik ;

  licheck_a[0] = licheck_a[1] = 0 ;

  for( ii=0 ; ii < 2 ; ii++ ) {
    for( ij=0 ; ij < 32 ; ij++ ) {
      ibit = (licseed_a[ii] >> ij) & 1 ;

      for( ik=0 ; ik < 10 ; ik++ )
        ipoly += (ipoly >> 26) + (ipoly >> 7) + (ipoly << 13) + ibit ;

      licheck_a[ii] |= ((ipoly & 1) << ij) ;
    }
  }
}
