/*
    FPGA_Comm.c, Copyright 1997 (c) by Bernard Stauffer,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.
 
    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.
 
    For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
    This software is provided under the terms of the GNU General Public Licence.
 
    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
 
  File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/IO/mips/Drivers/FPGA_Comm.c,v $
  Author(s):             Bernard Stauffer
  Affiliation:           ETH Zuerich, TIK
  Version:               $Revision: 2936 $
  Creation Date:         1997/05/02 20:34:00
  Last Date of Change:   $Date: 2007-03-21 15:23:42 +0100 (Mit, 21 Mär 2007) $      by: $Author: baderma $
*/


#include "Messages.h"
#include "IODevice.h"
#include "IOConsole.h"
#include "FPGA_Comm.h"
#include "FPGA_Prog/fpga_prog.h"

IODeviceDesc fpgaDesc = {
                          NULL, NULL,              /* pointers for doubly linked list */
                          "fpga",                  /* device name */
                          (Address)FPGA_BASE,      /* device base address */
                          NULL,                    /* buffer must be allocated by driver */
                          FPGA_CARDINT,            /* interrupt */
                          (InterruptHandler)devFPGA_interruptHandler,
                          /* interrupt handling routine */
                          0,                       /* thread id filled in after start */
                          devFPGA_read,            /* device read function */
                          devFPGA_write,           /* device write function */
                          devFPGA_close,           /* device close function */
                          devFPGA_init,            /* device init function */
                          NULL,                    /* private msg handler */
                          FALSE,                   /* initialisation state, normally FALSE */
                          NULL                     /* data extension */
                        };
volatile char *ptr_fpga;


/*----------------------------------------------------------------*/
/* Driver's private functions */

char fpga_readStatusGet(void)
{
  ptr_fpga = (char*) FPGA_STATUSGET;
  return *ptr_fpga;
}

char fpga_readStatusPut(void)
{
  ptr_fpga = (char*) FPGA_STATUSPUT;
  return *ptr_fpga;
}

void fpga_putData(char c)
{
  ptr_fpga = (char*) FPGA_DATAPUT;
  *ptr_fpga = c;
}

char fpga_getData(void)
{
  ptr_fpga = (char*) FPGA_DATAGET;
  return *ptr_fpga;
}


/*----------------------------------------------------------------*/
void devFPGA_interruptHandler(IODevice this)
{

  devFPGA   fpga_comm;
  char      lost;

  fpga_comm = (devFPGA) this->extension;

  while (fpga_readStatusGet() != 0)
  { /* Read as long data are present */

    if (((fpga_comm->in + 1) % FPGA_BUFSIZE) == fpga_comm->out)
    {
      lost = fpga_getData();          /* We have to get the data, even if full */
      WARNING("\nFPGA COMM receive overflow\n");
    }
    else
    {                          /* If buffer not full, copy data */
      this->buffer[fpga_comm->in] = fpga_getData();
      fpga_comm->in = ((fpga_comm->in + 1) % FPGA_BUFSIZE);
    }

  }
}


/*----------------------------------------------------------------*/
Error devFPGA_read(IODevice this, ThreadId threadId, char* buffer,
                   long int* size)
{
  devFPGA fpga_comm;
  long int     i = 0;


  fpga_comm = (devFPGA) this->extension;

  while (TRUE)
  {
    /* Stop, if no more characters available (buffer empty) */
    if (fpga_comm->in == fpga_comm->out)
    {
      break;
    }
    /* Stop, if user is satisfied */
    if (i >= *size)
    {
      break;
    }
    /* Copy data and increment data pointer */
    buffer[i++] = this->buffer[fpga_comm->out];
    fpga_comm->out = ((fpga_comm->out + 1) % FPGA_BUFSIZE);

  }

  *size = i;            /* Set the actual value of read chars */

  return IO_READOK;
}



/*----------------------------------------------------------------*/
Error devFPGA_write(IODevice this, ThreadId threadId, char* buffer,
                    long int* size)
{
  long int i = 0;

  for (i = 0; i < *size; i++)
  {
    while(fpga_readStatusPut() == 0)
    {}
    ;  /* Wait for FPGA board to become ready */

    fpga_putData(buffer[i]);              /* Send it */
  }

  return IO_WRITEOK;
}



/*----------------------------------------------------------------*/
Error devFPGA_init(IODevice this)
{

  devFPGA fpga_comm;


  if (!this->isInitialised)
  {

    hmAlloc((Address*)&(this->buffer), FPGA_BUFSIZE);           /* Allocate buffer and */
    hmAlloc((Address*)&(this->extension), sizeof(devFPGADesc)); /* ... descriptor space */

    InitLinkBOARD();                /* Programs and initializes the FPGA comm interface */

    this->isInitialised = TRUE;     /* Set Initialized flag */
  }

  fpga_comm = (devFPGA)this->extension;   /* Hook drivers vars in descriptor */
  fpga_comm->in = 0;                      /* Reset buffer pointers to initial value */
  fpga_comm->out = 0;

  return IO_INITOK;

}



/*----------------------------------------------------------------*/
Error devFPGA_close(IODevice this)
{

  /* this message is forwarded from ioThread */
  /* release buffers if any */


  if (this->isInitialised)
  {

    hmFree((Address)this->buffer);      /* Deallocate buffer space */
    hmFree((Address)this->extension);   /* Deallocate descriptor space */

    this->isInitialised = FALSE;        /* Set Initialized flag */
  }

  return IO_CLOSEOK;
}



/*----------------------------------------------------------------*/
