/* **********************************
 * *                                *  
 * *           DSP Driver           *
 * *                                *  
 * **********************************  
 * File: libs/dsp/dsp_read.c
 *
 *  COPYRIGHT (c) 1987, 1988, 1989, 1996, 1998, 1999, 2002, 2003, 2004,
 *                              2006 BY
 *             VAISALA INC., 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. 
 */
#define DSP_SCSI_INCLUDED 1

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#ifdef IRIX
#include <sys/dsreq.h>
#endif
#ifdef HPUX
#include <sys/scsi.h>
#endif

#include "sigtypes.h"  
#include "dsp.h"
#include "rda.h"
#include "config_lib.h"
#include "dsp_lib.h"  
#include "user_lib.h"

#include "dsp_def.h"  

/* ===================================================================== 
 * This routine is called to read from the signal processor.
 * Entry parameters: 
 *   DSP_A        DSP structure pointer
 *   IRBUF_A      Data to input 
 *   IRCNT_A      Count, word count 
 * Exit parameters:
 *   IACTWCNT_A   The actual number of words read
 */
MESSAGE dsp_read
( Dsp *dsp_a,
  volatile void *irbuf_a,
  SINT4 ircnt_a,
  volatile SINT4 *iactwcnt_a ) 
{      
  MESSAGE istatus = SS_NORMAL ;
  SINT4 iBytesWanted = (ircnt_a * 2), iBytesRead = 0 ;

  switch (dsp_a->interface)
    {      
      /* -------------------- SCSI Bus Interface -------------------- */
    case DSPIFACE_SCSI:
      {
	char *pbyte = (char *)irbuf_a ;
	
	/* Break the input transfer into individual reads of less than
	 * SCSI_MAX_IOLEN apiece, but without producing any short stubs less
	 * than 16-bytes long.
	 */
	while( (istatus == SS_NORMAL) && (iBytesWanted > 0) ) {
	  SINT4 ibytes_try = MIN( SCSI_MAX_IOLEN, iBytesWanted ) ;
	  SINT4 iBytesGot = 0 ;
	  UINT4 iobuf[1], *ioaddr = (UINT4 *)pbyte ;
	  
	  if( ((iBytesWanted - ibytes_try) >  0) &&
	      ((iBytesWanted - ibytes_try) < 16)  ) ibytes_try -= 16 ;
	  

	  /* If this is an unaligned read of four bytes or less, then do the
	   * I/O to an aligned buffer, and copy the data out of it later.
	   * Otherwise, give up on unaligned transfers.
	   */
	  if( CHECK_IO_ALIGNMENT && (0 != (3 & (UINT4)pbyte)) && (ibytes_try > 4) )
	    istatus = DSP_UNALIGN_RD ;
	  else {
	    if( (0 != (3 & (UINT4)pbyte)) && (ibytes_try < 4) ) ioaddr = iobuf ;

#ifdef HPUX /* ------------------------- HP Code ------------------------- */
	    {
	      struct sctl_io gkdesc ; SINT4 ii ;

	      memset( &gkdesc, 0, sizeof(gkdesc) ) ;
	      gkdesc.data_length    = ibytes_try ;
	      gkdesc.data           = (void *)ioaddr ;
	      gkdesc.max_msecs      = DSP_TIMEOUT_MS ;
	      gkdesc.flags          = SCTL_READ ;
	      gkdesc.cdb_length     = 6 ;

	      gkdesc.cdb[0] = GK_SCSI_RECEIVE ;
	      gkdesc.cdb[3] = (ibytes_try >> 8) ; /* Allocation length MSByte */
	      gkdesc.cdb[4] = ibytes_try & 255 ;  /* Allocation length LSByte */

	      ii = ioctl( dsp_a->idsplu, SIOC_IO, &gkdesc ) ;
	      if( ii < 0 ) {
		istatus = errno_to_message_va( errno, 1, "dsp_read()" ) ;
	      } else {
		istatus = hpux_scsi_status(&gkdesc) ;
		if( istatus == SS_NORMAL ) iBytesGot = ibytes_try ;
	      }
	    }
#endif /* #ifdef HPUX */

#ifdef IRIX /* ------------------------- SGI Code ------------------------- */
	    {
	      struct dsreq gkdesc ; SINT4 ii ; UINT1 cdb[6] ;

	      memset( &gkdesc, 0, sizeof(gkdesc) ) ;
	      gkdesc.ds_flags       = DSRQ_READ | DSRQ_DISC ;
	      gkdesc.ds_time        = DSP_TIMEOUT_MS ;
	      gkdesc.ds_cmdbuf      = (caddr_t)&cdb ;
	      gkdesc.ds_cmdlen      = sizeof(cdb) ;
	      gkdesc.ds_databuf     = (caddr_t)ioaddr ;
	      gkdesc.ds_datalen     = ibytes_try ;

	      memset( &cdb, 0, sizeof(cdb) ) ;
	      cdb[0] = GK_SCSI_RECEIVE ;
	      cdb[3] = (ibytes_try >> 8) ; /* Allocation length MSByte */
	      cdb[4] = ibytes_try & 255 ;  /* Allocation length LSByte */

	      ii = ioctl( dsp_a->idsplu, DS_ENTER, &gkdesc ) ;
	      if( ii < 0 ) {
		istatus = errno_to_message_va( errno, 1, "dsp_read()" ) ;
	      } else {
		istatus = irix_scsi_status(&gkdesc) ;
		if( istatus == SS_NORMAL ) iBytesGot = ibytes_try ;
	      }
	    }
#endif /* #ifdef IRIX */

#ifdef LINUX /* ----------------------- LINUX Code ----------------------- */
	    {
	      UINT1 cdb[6] ;

	      memset( &cdb, 0, sizeof(cdb) ) ;
	      cdb[0] = GK_SCSI_RECEIVE ;
	      cdb[3] = (ibytes_try >> 8) ; /* Allocation length MSByte */
	      cdb[4] = ibytes_try & 255 ;  /* Allocation length LSByte */

	      istatus = linux_scsi_command
		( dsp_a->idsplu, ioaddr, ibytes_try, TRUE, cdb ) ;

	      if( istatus == SS_NORMAL ) iBytesGot = ibytes_try ;
	    }
#endif /* #ifdef LINUX */

	    /* Copy the data out if they were actually read into
	     * the alternate aligned buffer.
	     */
	    if( (iBytesGot != 0) && (ioaddr == iobuf) )
	      memcpy( pbyte, ioaddr, iBytesGot ) ;
	  }
	  /* Bump buffer pointer, total bytes read, and remaining byte
	   * count according to what we just read.
	   */
	  pbyte        += iBytesGot ;
	  iBytesRead   += iBytesGot ;
	  iBytesWanted -= iBytesGot ;
	}
      }
      break ;
      
      /* -------------------- Native Interface -------------------- */
    case DSPIFACE_NATIVE:
      {
	/* The read() from the RDA kernel FIFOs may return the
	 * available data in chunks that are smaller than the read
	 * size.  Therefore, glue them all together ourselves.
	 */
	char *pbyte = (char *)irbuf_a ;
	while( (istatus == SS_NORMAL) && (iBytesWanted > 0) ) {
	  int iBytesGot = read( dsp_a->idsplu, pbyte, iBytesWanted ) ;
	  if( iBytesGot < 0 ) {
	    istatus = errno_to_message_va( errno, 1, "dsp_read()" ) ;
	  } else {
	    pbyte        += iBytesGot ;
	    iBytesRead   += iBytesGot ;
	    iBytesWanted -= iBytesGot ;
	  }
	}
      }
      break ;
      
      /* -------------------- Socket Interface -------------------- */
    case DSPIFACE_SOCKET:
      {
	char sCommand[40]; int iCount;
	
	sprintf( sCommand, "READ|%d|", iBytesWanted );
	istatus = WriteSocketString( dsp_a->idsplu, sCommand, FALSE );
	
	if (istatus==SS_NORMAL)
	  {
	    iCount = ReadSocket( dsp_a->idsplu, (char *)irbuf_a, iBytesWanted, &istatus );
	    if (istatus==SS_NORMAL)
	      {
		if( iCount >= 0 ) iBytesRead = iCount ;
		if (iCount != iBytesWanted)
		  istatus = iMessageMod_va( DSP_READ_PACKET_SIZE, 3, "Got:%d, Wanted:%d",
					    iCount, iBytesWanted );
	      }
	  }
      }
      break ;
      
      /* -------------------- Unknown Interface -------------------- */
    default:
      istatus = DSP_CASE_IFACE ;
      break ;
    }
  
  /* Return actual word count and status.
   */
  *iactwcnt_a = (iBytesRead / 2) ;
  return( istatus ) ;
}

/* ===================================================================== 
 * This routine is called to read from the signal processor.  It reads
 * a variable number of words, based on the data available bit from
 * the signal processor.  
 *
 * Note that this is not guaranteed to work if you have recently sent
 * the FIFO reset command to the RVP.  This is because the FIFO reset
 * could take effect between the dspr_status() call and the dsp_read()
 * call.
 */
MESSAGE DspReadAvail
( Dsp *pDsp_a,
  volatile void *irbuf_a,      /* Data goes here */
  SINT4 ircnt_a,               /* Maximum size of the transfer */
  SINT4 iDmaSize_a,            /* Size of individual reads */
  volatile SINT4 *iactwcnt_a ) /* Actual size of the transfer */
{      
  MESSAGE istatus = SS_NORMAL ;
  SINT4 iWordsRead=0;
  SINT2 *pBuffer = (SINT2*)irbuf_a;

  switch (pDsp_a->interface)
    {      
      /* -------------------- Most Interfaces -------------------- */
    case DSPIFACE_SCSI:
    case DSPIFACE_NATIVE:
      {
	UINT2 istat;
	SINT4 iActCount, iReadSize;
	while( iWordsRead < ircnt_a )
	  {
	    istatus = dspr_status( pDsp_a, &istat ) ;
	    if( istatus != SS_NORMAL ) break ;
	    
	    if( 0 == (istat & DSPSTS_OUTAVAIL) ) break;

	    iReadSize = ircnt_a - iWordsRead ; iReadSize = MIN( iReadSize, iDmaSize_a ) ;
	    istatus = dsp_read( pDsp_a, &pBuffer[iWordsRead], iReadSize, &iActCount ) ;
	    if( istatus != SS_NORMAL ) break ;
	    iWordsRead += iActCount ;
	  }
      }
      break ;
      
      /* -------------------- Socket Interface -------------------- */
    case DSPIFACE_SOCKET:
      {
	char sCommand[40]; int iCount;
	
	sprintf( sCommand, "RDAV|%d|%d|", 2*ircnt_a, 2*iDmaSize_a );
	istatus = WriteSocketString( pDsp_a->idsplu, sCommand, FALSE );
	
	if (istatus==SS_NORMAL)
	  {
	    iCount = ReadSocket( pDsp_a->idsplu, (char *)pBuffer, 2*ircnt_a, &istatus );
	    if( iCount >= 0 ) iWordsRead = iCount/2 ;
	  }
      }
      break ;
      
      /* -------------------- Unknown Interface -------------------- */
    default:
      istatus = DSP_CASE_IFACE ;
      break ;
    }
  
  /* Return actual word count and status.
   */
  *iactwcnt_a = iWordsRead;
  return( istatus ) ;
}

/* ======================================================================
 * This routine is called to read the status lines from the DSP
 *     
 * Entry parameters
 *   DSP_A      DSP structure pointer
 * Exit parameters:
 *   istats_a   Status bits in low three bits (See DSPSTS_XXX)
 */
MESSAGE dspr_status
( Dsp *dsp_a,
  UINT2 *pStat_a )
{
  MESSAGE istatus = SS_NORMAL ;
      
  switch( dsp_a->interface )
    {
    /* -------------------- SCSI Bus Interface --------------------
     *         
     * Issue a MODE SENSE command to get the equiavalent of what
     * the parallel interface is able to read on separate wires.
     */
    case DSPIFACE_SCSI:
      {
	UINT1 gkdata[10] ; 

#ifdef HPUX /* ------------------------- HP Code ------------------------- */
	{
	  struct sctl_io gkdesc ; SINT4 ii ;

	  memset( &gkdesc, 0, sizeof(gkdesc) ) ;
	  gkdesc.data_length    = sizeof(gkdata) ;
	  gkdesc.data           = (void *)&gkdata ;
	  gkdesc.max_msecs      = DSP_TIMEOUT_MS ;
	  gkdesc.flags          = SCTL_READ ;
	  gkdesc.cdb_length     = 6 ;

	  gkdesc.cdb[0] = GK_SCSI_MODESENSE ;
	  gkdesc.cdb[4] = sizeof(gkdata) ;

	  ii = ioctl( dsp_a->idsplu, SIOC_IO, &gkdesc ) ;
	  if( ii < 0 ) {
	    istatus = errno_to_message_va( errno, 1, "dspr_status()" ) ;
	  } else {
	    istatus = hpux_scsi_status(&gkdesc) ;
	  }
	}
#endif /* #ifdef HPUX */
	  
#ifdef IRIX /* ------------------------- SGI Code ------------------------- */
	{
	  struct dsreq gkdesc ; SINT4 ii ; UINT1 cdb[6] ;

	  memset( &gkdesc, 0, sizeof(gkdesc) ) ;
	  gkdesc.ds_flags       = DSRQ_READ | DSRQ_DISC ;
	  gkdesc.ds_time        = DSP_TIMEOUT_MS ;
	  gkdesc.ds_cmdbuf      = (caddr_t)&cdb ;
	  gkdesc.ds_cmdlen      = sizeof(cdb) ;
	  gkdesc.ds_databuf     = (caddr_t)&gkdata ;
	  gkdesc.ds_datalen     = sizeof(gkdata) ;

	  memset( &cdb, 0, sizeof(cdb) ) ;
	  cdb[0] = GK_SCSI_MODESENSE ;
	  cdb[4] = sizeof(gkdata) ;

	  ii = ioctl( dsp_a->idsplu, DS_ENTER, &gkdesc ) ;
	  if( ii < 0 ) {
	    istatus = errno_to_message_va( errno, 1, "dspr_status()" ) ;
	  } else {
	    istatus = irix_scsi_status(&gkdesc) ;
	  }
	}
#endif /* #ifdef IRIX */

#ifdef LINUX /* ----------------------- LINUX Code ----------------------- */
	{
	  UINT1 cdb[6] ;

	  memset( &cdb, 0, sizeof(cdb) ) ;
	  cdb[0] = GK_SCSI_MODESENSE ;
	  cdb[4] = sizeof(gkdata) ;

	  istatus = linux_scsi_command
	    ( dsp_a->idsplu, gkdata, sizeof(gkdata), TRUE, cdb ) ;
	}
#endif /* #ifdef LINUX */
	  
	*pStat_a = ((gkdata[0] !=0) || (gkdata[1] !=0)) ? 1 : 0 ;
      }
    break ;

    /* -------------------- Native Interface -------------------- */
    case DSPIFACE_NATIVE:
      {
	struct rvpio_status rvpStatus ;
	if( ioctl( dsp_a->idsplu, RVPIOC_STATUS, &rvpStatus ) < 0 ) {
	  istatus = errno_to_message_va( errno, 1, "dspr_status()" ) ;
	} else {
	  *pStat_a =
	    ( rvpStatus.rdFIFO.iUsed ? DSPSTS_OUTAVAIL : 0 ) |
	    ( rvpStatus.wrFIFO.iUsed ? DSPSTS_INQUEUED : 0 ) ;
	}
      }
      break ;

      /* -------------------- Socket Interface -------------------- */
    case DSPIFACE_SOCKET:
      {
	istatus = WriteSocketString( dsp_a->idsplu, "STAT|", FALSE );
	if (istatus==SS_NORMAL)
	  {
	    char sBuffer[40];
	    ReadSocket( dsp_a->idsplu, sBuffer, 40, &istatus );
	    if (istatus==SS_NORMAL)
	      {
		char *pEnd;
		sBuffer[39]=0;  /* Must be null terminated */
		*pStat_a = strtol( sBuffer, &pEnd, 10 );
	      }
	  }
      }
      break ;

    /* -------------------- Unknown Interface -------------------- */
    default:
      istatus  = DSP_CASE_IFACE ;
      break ;
    }

  return( istatus ) ;
}
/* ===================================================================== 
 * This routine is called to read the time from a remote RVP8.
 */
MESSAGE DspGetTimeFull
( Dsp *pDsp_a,
  struct full_time_spec *pTime_a,
  int iFlags_a )
{      
  MESSAGE istatus = SS_NORMAL ;
  
  switch (pDsp_a->interface)
    {      
    default:
      GetTimeFull( pTime_a, iFlags_a );
      break;

      /* -------------------- Socket Interface -------------------- */
    case DSPIFACE_SOCKET:
      {
	char sCommand[40];
	
	sprintf( sCommand, "TIME|%d|", iFlags_a );
	istatus = WriteSocketString( pDsp_a->idsplu, sCommand, FALSE );
	
	if (istatus==SS_NORMAL)
	  
	  {
	    struct lim_memory Ascii;
	    /* Read back the setup structure */
	    Ascii.sPointer = ReadSocketPacketTcp( pDsp_a->idsplu, &Ascii.iSize, FALSE,
						  DSP_TIMEOUT_MS, NULL );
	    istatus = CheckSocketAck( Ascii.sPointer, Ascii.iSize );
	    if (istatus==SS_NORMAL)
	      {
		struct lim_memory AsciiDup;
		AsciiDup = Ascii;
		AsciiDup.sPointer+=4;  /* Skip the "Ack|" */
		AsciiDup.iSize   -=4;
		/* Convert to binary structure */
		istatus = lim_loadMemory( &AsciiDup, "full_time_spec", pTime_a,
					  setup_limits );
	      }
	    free( Ascii.sPointer );
	  }
      }
      break ;
    }
  
  return( istatus ) ;
}
