/*
    SA1100_UART.c, Copyright  (c) by Silvio Dragone & David Reist,
    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.
*/

#include "Drivers/SA1100_UART.h"
#include "Messages.h"
#include "IODevice.h"
#include "IOHal.h"
#include "Lock.h"
#include "Configuration.h"
#include "cpu.h"
#include "Exception.h"

//[SD]
//#include "Support.h"

IODeviceDesc sa1100_uart_a = {
                               NULL, NULL,                                        /* pointers for doubly linked list */
                               "ttya",                                            /* device name */
                               (Address)UART_DR_REGISTER,                         /* device base address */
                               NULL,                                              /* buffer must be allocated by driver */
                               UART_I,                                            /* interrupt */
                               (InterruptHandler)devSA1100_UART_interruptHandler, /* ttya interrupt handler */
                               0,                                                 /* thread id filled in after start */
                               devSA1100_UART_read,                               /* device read function */
                               devSA1100_UART_write,                              /* device write function */
                               devSA1100_UART_close,                              /* device close function */
                               devSA1100_UART_init,                               /* device init function */
                               NULL,                                              /* private msg handler */
                               FALSE,                                             /* initialisation state, normally FALSE */
                               NULL                                               /* data extension */
                             };


void devSA1100_UART_interruptHandler(IODevice this)
{
  devFIFO_Ptr fifo_ptr;
  unsigned long* rx_data = (unsigned long*) UART_DR_REGISTER;
  unsigned long int* status = (unsigned long int*) UART_SR_REGISTER;
  unsigned long int* control = (unsigned long int*) UART_CR_REGISTER;
  unsigned long int tmp = 0;
  unsigned long c = 0;

  printf("devSA1100_UART_interruptHandler:\n");
  fifo_ptr = (devFIFO_Ptr)(this->extension);

  do
  {
    tmp=*status;
    //-CJ
    printf("\ttesting if ready %p %#8lx control %p %#lx data %p %#lx\n", status, tmp, control, *control, rx_data, *rx_data);
    tmp &= RX_FIFO_READY;
  }
  while(!tmp);
  printf("\tdevice if ready\n");

  while (tmp)
  {
    if ((fifo_ptr->in + 1) % SA1100_UART_BUFSIZE == fifo_ptr->out)
    {
      WARNING("UART overflow\n");
      return;
    }
    c = *rx_data;
    this->buffer[fifo_ptr->in] = (char)c;

    fifo_ptr->in = (fifo_ptr->in + 1) % SA1100_UART_BUFSIZE;
    tmp=*status & RX_FIFO_READY;
  }
  printf("read c: %li=%c\n",c ,(int)c);
  //printf("fifo_ptr = %p, fifo_ptr->in = %p == fifo_ptr->out = %p\n",
  // fifo_ptr, fifo_ptr->in, fifo_ptr->out);
}


Error devSA1100_UART_init(IODevice this)
{
  unsigned long int* control = (unsigned long int*) UART_CR_REGISTER;
  devFIFO_Ptr fifo_ptr;

  /* device init */
  *control = *control & ~UART_ENABLE;                                 /* disable UART */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = *control & ~PARITY_ENABLE;                               /* no parity */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = (*control & ~DATA_SIZE_SELECT_MASK) | (0x3 << 5);        /* 8 bits/char */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = *control & ~STOP_BIT_SELECT;                             /* 1 stop bit */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = (*control & ~BAUD_RATE_DIVISOR_MASK) | (0x17 << 16);     /* 9600 baud */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = *control & ~UART_INTERRUPT_SELECT;                       /* IRQ UART interrupt */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = (*control | RX_INTERRUPT_ENABLE) & ~TX_INTERRUPT_ENABLE; /* Rx interrupt enabled, Tx interrupt disabled */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  *control = *control | UART_ENABLE;                                  /* enable UART */
  ioDelayAtLeastCycles( NBCYCLESFORDELAY);

  /* buffers init - be careful to check for existing buffers */
  if (!this->isInitialised)
  {
    hmAlloc(&(this->extension), 2); /* IN & OUT */
    hmAlloc((Address*)&(this->buffer), SA1100_UART_BUFSIZE);
    fifo_ptr = (devFIFO_Ptr)this->extension;
    this->isInitialised = TRUE;
  }
  else
  {
    fifo_ptr = (devFIFO_Ptr)this->extension;
  }
  fifo_ptr->in = 0;
  fifo_ptr->out = 0;

  return IO_INITOK;
}


Error devSA1100_UART_read(IODevice this, ThreadId threadId, char* buffer,
                          long int* size)
{
  long int i = 0;
  devFIFO_Ptr fifo_ptr;

  fifo_ptr = (devFIFO_Ptr)this->extension;
  //printf("buffer@%p[i=%i < *size=%i]\n", buffer, i, *size);
  while (TRUE)
  {
    /* no more characters available, buffer empty
     */
    //    printf("fifo_ptr = %p, fifo_ptr->in = %p == fifo_ptr->out = %p\n",
    //fifo_ptr, fifo_ptr->in, fifo_ptr->out);
    if (fifo_ptr->in == fifo_ptr->out)
    {
      break;
    }
    /* user is satisfied
     */
    if (i >= *size)
    {
      break;
    }

    //printf("BUFFER@%p[i=%i < *size=%i]\n", buffer, i, *size);
    buffer[i++] = this->buffer[fifo_ptr->out];
    //printf("BUFFER@%p[i=%i < *size=%i]\n", buffer, i, *size);
    fifo_ptr->out = (fifo_ptr->out + 1) % SA1100_UART_BUFSIZE;
  }
  *size = i;

  //[SD]
  /*if (i>0) {
  int q = 0;
    printf("\nReading UART-buffer: %p = ", buffer);
    for (q = 0; q < i; q++) {
      printf("HALLO\n");
      printf("%i=%c -- \n", buffer[q], buffer[q]);
    }
    printf("\n");
    }*/
  //printf("about to return\n");
  return IO_READOK;
}


Error devSA1100_UART_write(IODevice this, ThreadId threadId, char* buffer, long int* size)
{
  char* tx_data = (char*) UART_DR_REGISTER;
  unsigned long int* status  = (unsigned long int*) UART_SR_REGISTER;
  long int i = 0;

  //[SD]
  //printf("\nIs writing to the UART-buffer!\n");

  for (i = 0; i < *size; i++)
  {
    while ((*status & TX_FIFO_READY) != TX_FIFO_READY)
    {
      ioDelayAtLeastCycles( NBCYCLESFORDELAY);
    }				/* wait for uart to become ready */
    *tx_data = buffer[i];		/* send it finally */

    ioDelayAtLeastCycles( NBCYCLESFORDELAY);
  }
  return IO_WRITEOK;
}


Error devSA1100_UART_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;
}
