/*
 *      ISERVER  -  INMOS standard file server
 *
 *      b014link.c
 *
 *      Link module for B014 boards with S514 device driver
 *
 *      Copyright (c) INMOS Ltd., 1988.
 *      All Rights Reserved.
 */


/* Modification:
 * 31/08/90 BJ  Thorough overhaul - checking return from 'ioctl'
 *                                - use IMS_IO instead of B014_IO
 *                                - add 'lseek' in 'ReadLink' & 'WriteLink'
 *                                - fix timeout return code
 */

#include <stdio.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/file.h>      /* Declarations for 'lseek' */

#include "ims_bcmd.h"      /* INMOS device driver 'ioctl' codes */

#include "inmos.h"
#include "iserver.h"

#include <errno.h>

/* #define TRACE */

#define NULL_LINK          -1
#define REWIND_THRESHOLD   (1024L * 1024L)

static LINK ActiveLink = NULL_LINK;
static long int Bytes = 0L;


/* OpenLink --- open a link to the transputer */

LINK OpenLink (Name)
BYTE *Name;
{
   static char DefaultDevice[] = "/dev/bxiv0";

   /* Already open ? */
   if (ActiveLink != NULL_LINK)
      return (ER_LINK_CANT);

   /* Use default name ? */
   if ((Name == NULL) || (*Name == '\0')) {
      if ((ActiveLink = open (DefaultDevice, O_RDWR)) >= 0)
         return (ActiveLink);
   }
   else {
      if ((ActiveLink = open (Name, O_RDWR)) >= 0)
         return (ActiveLink);
   }

   if (errno == EBUSY)
      return (ER_LINK_BUSY);
   else if (errno == ENOENT)
      return (ER_LINK_SYNTAX);
   else if ((errno == ENXIO) || (errno == ENODEV))
      return (ER_NO_LINK);
   else
      return (ER_LINK_CANT);
}


/* CloseLink --- close down the link connection */

int CloseLink (LinkId)
LINK LinkId;
{
/* Note: On a Sun-4, 'close' always returns EPERM, so the code below
         simply ignores the return value.  Kludge of the millenium */

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   ActiveLink = NULL_LINK;

   if (close (LinkId) == -1)
      return (SUCCEEDED);     /* Should be ER_LINK_CANT */

   return (SUCCEEDED);
}


/* ReadLink --- */

int ReadLink (LinkId, Buffer, Count, Timeout)
LINK LinkId;
char *Buffer;
unsigned int Count;
int Timeout;
{
   register int ret;

#ifdef TRACE
   printf ("ReadLink: %d bytes requested\n", Count);
#endif

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   if (Count < 1)
      return (ER_LINK_CANT);

   if (!SetTimeout (LinkId, Timeout))
      return (ER_LINK_CANT);

   ret = read (LinkId, Buffer, Count);

   if (ret == -1)
      ret = ER_LINK_CANT;
   else {
      Bytes += (long int)ret;
      
      /* Rewind if we've sent enough */
      if (Bytes > REWIND_THRESHOLD) {
         if (lseek (LinkId, 0L, L_SET) == -1L)
            ret = ER_LINK_CANT;
            
         Bytes = 0L;
      }
   }

#ifdef TRACE
   printf ("ReadLink: %d bytes read\n", ret);
#endif

   return (ret);
}   


/* WriteLink --- */

int WriteLink (LinkId, Buffer, Count, Timeout)
LINK LinkId;
char *Buffer;
unsigned int Count;
int Timeout;
{
   register int ret;

#ifdef TRACE
   printf ("WriteLink: %d bytes requested\n", Count);
#endif

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   if (Count < 1)
      return (ER_LINK_CANT);

   if (!SetTimeout (LinkId, Timeout))
      return (ER_LINK_CANT);

   ret = write (LinkId, Buffer, Count);

   if (ret == -1)
      ret = ER_LINK_CANT;
   else {
      Bytes += (long int)ret;
      
      /* Rewind if we've sent enough */
      if (Bytes > REWIND_THRESHOLD) {
         if (lseek (LinkId, 0L, L_SET) == -1L)
            ret = ER_LINK_CANT;
            
         Bytes = 0L;
      }
   }

#ifdef TRACE
   printf ("WriteLink: %d bytes written\n", ret);
#endif

   return (ret);
}


/* SetTimeout --- set the timeout duration */

static BOOL SetTimeout (LinkId, Timeout)
LINK LinkId;
int Timeout;
{
   union IMS_IO io;
   static int TheCurrentTimeout = -1;

   if (Timeout != TheCurrentTimeout) {
      io.set.op = SETTIMEOUT;
      io.set.val = Timeout;
      if (ioctl (LinkId, SETFLAGS, &io) == -1)
         return (FALSE);

      TheCurrentTimeout = Timeout;
   }

   return (TRUE);
}


/* ResetLink --- */

int ResetLink (LinkId)
LINK LinkId;
{
   union IMS_IO io;

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   io.set.op = RESET;

   if (ioctl (LinkId, SETFLAGS, &io) == -1) {
#ifdef DB
      printf ("errno = %d\n", errno);
#endif
      return (ER_LINK_CANT);
   }

   return (SUCCEEDED);
}


/* AnalyseLink --- */

int AnalyseLink (LinkId)
LINK LinkId;
{
   union IMS_IO io;

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   io.set.op = ANALYSE;

   if (ioctl (LinkId, SETFLAGS, &io) == -1) {
#ifdef DB
      printf ("errno = %d\n", errno);
#endif
      return (ER_LINK_CANT);
   }

   return (SUCCEEDED);
} 


/* TestError --- */

int TestError (LinkId)
LINK LinkId;
{
   union IMS_IO io;

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   if (ioctl (LinkId, READFLAGS, &io) == -1) {
#ifdef DB
      printf ("errno = %d\n", errno);
#endif
      return (ER_LINK_CANT);
   }

   return ((int) io.status.error_f);
}  


/* TestRead --- */

int TestRead (LinkId)
LINK LinkId;
{
   union IMS_IO io;

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   if (ioctl (LinkId, READFLAGS, &io) == -1) {
#ifdef DB
      printf ("errno = %d\n", errno);
#endif
      return (ER_LINK_CANT);
   }

   return ((int) io.status.read_f);
}


/* TestWrite --- */

int TestWrite (LinkId)
LINK LinkId;
{
   union IMS_IO io;

   if (LinkId != ActiveLink)
      return (ER_LINK_BAD);

   if (ioctl (LinkId, READFLAGS, &io) == -1) {
#ifdef DB
      printf ("errno = %d\n", errno);
#endif
      return (ER_LINK_CANT);
   }

   return ((int) io.status.write_f);
}
