/* **********************************
 * *                                *  
 * *           DSP Driver           *
 * *                                *  
 * **********************************  
 * File: libs/dsp/dsp_write.c
 *
 *   COPYRIGHT (c) 1988, 1989, 1990, 1994, 1998, 1999, 2003, 2004 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. 
 */
#define DSP_SCSI_INCLUDED 1

#include <string.h>

#include <errno.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 "user_lib.h"
#include "dsp_lib.h"

#include "dsp_def.h"

/* ======================================================================
 * This routine is called to do the actual output to a DSP.
 * History:
 *
 * Entry parameters: 
 *   DSP_A     DSP to use
 *   IWBUF_A   Data to output
 *   IWCNT_A   Count, (positive word count)
 */
MESSAGE dsp_write
( Dsp *dsp_a,
  volatile const void *iwbuf_a, 
  SINT4 iwcnt_a ) 
{
  MESSAGE istatus = SS_NORMAL ;
  SINT4 iBytesRemain = (iwcnt_a * 2) ;

  switch( dsp_a->interface )
    {
      /* -------------------- SCSI Bus Interface -------------------- */
    case DSPIFACE_SCSI:
      {
	char *pbyte = (char *)iwbuf_a ;

	/* Break the output transfer into individual writes of less than
	 * SCSI_MAX_IOLEN apiece, but without producing any short stubs less
	 * than 16-bytes long.
	 */
	while( (istatus == SS_NORMAL) && (iBytesRemain > 0) ) {
	  SINT4 iBytesTry = MIN( SCSI_MAX_IOLEN, iBytesRemain ) ;
	  SINT4 iBytesPut = 0 ;
	  UINT4 iobuf[1], *ioaddr = (UINT4 *)pbyte ;
	  
	  if( ((iBytesRemain - iBytesTry) >  0) &&
	      ((iBytesRemain - iBytesTry) < 16)  ) iBytesTry -= 16 ;
	  
	  /* If this is an unaligned write of four bytes or less, then do the
	   * I/O from an aligned buffer that has the data first copied into it.
	   * Otherwise, give up on unaligned transfers.
	   */
	  if( CHECK_IO_ALIGNMENT && (0 != (3 & (UINT4)pbyte)) && (iBytesTry > 4) )
	    istatus = DSP_UNALIGN_WR ;
	  else {
	    if( (0 != (3 & (UINT4)pbyte)) && (iBytesTry <= 4) ) {
	      ioaddr = iobuf ; memcpy( ioaddr, pbyte, iBytesTry ) ;
	    }

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

	      memset( &gkdesc, 0, sizeof(gkdesc) ) ;
	      gkdesc.data_length    = iBytesTry ;
	      gkdesc.data           = (void *)ioaddr ;
	      gkdesc.max_msecs      = DSP_TIMEOUT_MS ;
	      gkdesc.flags          = 0 ; /* No read ==> write */
	      gkdesc.cdb_length     = 6 ;

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

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

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

	      memset( &gkdesc, 0, sizeof(gkdesc) ) ;
	      gkdesc.ds_flags       = DSRQ_WRITE | 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     = iBytesTry ;

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

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

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

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

	      istatus = linux_scsi_command
		( dsp_a->idsplu, ioaddr, iBytesTry, FALSE, cdb ) ;

	      if( istatus == SS_NORMAL ) iBytesPut = iBytesTry ;
	    }
#endif /* #ifdef LINUX */

	  }
	  /* Bump remaining byte count and buffer pointer according to
	   * what we've been able to write so far.
	   */
	  pbyte        += iBytesPut ;
	  iBytesRemain -= iBytesPut ;
	}
      }
      break ;
      
      /* -------------------- Native Interface -------------------- */
    case DSPIFACE_NATIVE:
      {
	/* The write() to the RDA kernel FIFOs may gobble the
	 * available data in chunks that are smaller than the full
	 * buffer size.  Therefore, glue them all together ourselves.
	 */
	char *pbyte = (char *)iwbuf_a ;
	while( (istatus == SS_NORMAL) && (iBytesRemain > 0) ) {
	  int iBytesPut = write( dsp_a->idsplu, pbyte, iBytesRemain ) ;
	  if( iBytesPut < 0 ) {
	    istatus = errno_to_message_va( errno, 1, "dsp_write()" ) ;
	  } else {
	    pbyte        += iBytesPut ;
	    iBytesRemain -= iBytesPut ;
	  }
	}
      }
      break ;
      
      /* -------------------- Socket Interface -------------------- */
    case DSPIFACE_SOCKET:
      {
	istatus = WriteSocketData2( dsp_a->idsplu, "WRIT|", 5, (void *)iwbuf_a,
				    iBytesRemain, FALSE );
	if (istatus==SS_NORMAL)
	  {
	    /* Read back the acknowledge */
	    istatus = ReadSocketAck( dsp_a->idsplu, FALSE, DSP_TIMEOUT_MS );
	  }
      }
      break ;
      
      /* -------------------- Unknown Interface -------------------- */
    default:
      istatus = DSP_CASE_IFACE ;
      break ;
    }
  
  return( istatus ) ;
}
