/*!
  \file
  \brief SCI の初期化

  \author Satofumi KAMIMURA

  $Id: sci_control.c 1504 2009-11-10 23:12:50Z satofumi $

  \todo 割り込みでの通信が適切に動作しないのを修正する
*/

#include "sci_control.h"
#include "cpu_aki_sh2.h"
#ifdef SCI_USE_INTERRUPT
#include "ring_buffer.h"
#include "imask_control.h"
#endif

#ifdef SCI_USE_INTERRUPT
static char SendBuffer[2][SCI_BUFFER_SIZE];
static char RecvBuffer[2][SCI_BUFFER_SIZE];
static ringBuffer_t RingSend[2];
static ringBuffer_t RingRecv[2];
static unsigned char Level[2];


static void init_buffer(sci_port port)
{
  ring_initialize(&RingSend[port], SendBuffer[port], SCI_BUFFER_SIZE_SHIFT);
  ring_initialize(&RingRecv[port], RecvBuffer[port], SCI_BUFFER_SIZE_SHIFT);
}
#endif


void sci_initialize(sci_port port, int level)
{
  (void)level;
  volatile int i;

  enum {
    /* 使うボーレート中の最大の値を代表値として用いる */
    SCI_BIT_WAIT = (int)(CPU_CLOCK / 115200),
  };

#ifdef SCI_USE_INTERRUPT
  // 割り込みバッファの初期化
  init_buffer(port);
#endif

  /* 指定ポートの初期化 */
  if (port == SCI_0) {
    /* SCI0 の初期化 */
    SCI0.SCR.BYTE = 0x00; /* TE=0, RE=0 */
    SCI0.SMR.BYTE = 0x00; /* 8bit, 1stop bit, no parity, TE=0, RE=0 */
    SCI0.BRR = 7;         /* 115200 bps */
    PFC.PACRL2.WORD |= 0x0005;  /* use TXD0, RXD0 function */

  } else if (port == SCI_1) {
    /* SCI1 の初期化 */
    SCI1.SCR.BYTE = 0x00; /* TE=0, RE=0 */
    SCI1.SMR.BYTE = 0x00; /* 8bit, 1stop bit, no parity, TE=0, RE=0 */
    SCI1.BRR = 7;         /* 115200 bps */
    PFC.PACRL2.WORD |= 0x0140;  /* use TXD1, RXD1 function */
  }

  for (i = 0; i < SCI_BIT_WAIT; ++i)
    ;

  if (port == SCI_0) {
    SCI0.SSR.BYTE &= ~0x40;
#ifdef SCI_USE_INTERRUPT
    Level[0] = level;
    INTC.IPRF.WORD &= ~0x00f0;
    INTC.IPRF.WORD |= (level << 4) & 0x00f0;
    SCI0.SCR.BYTE |= 0x40;
#endif
    SCI0.SCR.BYTE |= 0x30;

  } else if (port == SCI_1) {
    SCI1.SSR.BYTE &= ~0x40;
#ifdef SCI_USE_INTERRUPT
    Level[1] = level;
    INTC.IPRF.WORD &= ~0x000f;
    INTC.IPRF.WORD |= level & 0x000f;
    SCI1.SCR.BYTE |= 0x40;
#endif
    SCI1.SCR.BYTE |= 0x30;
  }
}

#ifdef SCI_USE_INTERRUPT


int sci_write(sci_port port, const char *data, int size)
{
  int original_level = imask_maskLevel();
  int n;

  // 送信データの格納
  imask_setMaskLevel(Level[port]);
  n = ring_write(&RingSend[port], data, size);

  if (n > 0) {
    // 送信割り込みの許可
    if (port == SCI_0) {
      SCI0.SCR.BYTE |= 0x80;
    } else if (port == SCI_1) {
      SCI1.SCR.BYTE |= 0x80;
    }
  }
  imask_setMaskLevel(original_level);

  return n;
}


int sci_read(sci_port port, char *buffer, int size)
{
  int original_level = imask_maskLevel();
  int read_size = 0;

  imask_setMaskLevel(Level[port]);
  read_size = ring_read(&RingRecv[port], buffer, size);
  imask_setMaskLevel(original_level);

  return read_size;
}


int sci_readable(sci_port port)
{
  int original_level = imask_maskLevel();
  int n;

  imask_setMaskLevel(Level[port]);
  n = ring_size(&RingRecv[port]);
  imask_setMaskLevel(original_level);

  return n;
}


void sci_stop(sci_port port)
{
  // 割り込みの禁止
  int original_level = imask_maskLevel();

  imask_setMaskLevel(Level[port]);
  if (port == SCI_0) {
    INTC.IPRF.WORD &= ~0x00f0;
    SCI0.SCR.BYTE &= ~0x40;

  } else if (port == SCI_1) {
    INTC.IPRF.WORD &= ~0x000f;
    SCI1.SCR.BYTE &= ~0x40;
  }
  imask_setMaskLevel(original_level);
}


int sci_isFlushed(sci_port port)
{
  if (port == SCI_0) {
    if ((SCI0.SCR.BYTE & 0x80) == 0) {
      return 1;
    }
  } else if (port == SCI_1) {
    if ((SCI1.SCR.BYTE & 0x80) == 0) {
      return 1;
    }
  }
  return 0;
}


#pragma interrupt
void eri0(void)
{
  SCI0.SSR.BYTE &= ~0x38;
}


#pragma interrupt
void eri1(void)
{
  SCI1.SSR.BYTE &= ~0x38;
}


#pragma interrupt
void rxi0(void)
{
  char ch = SCI0.RDR;
  ring_write(&RingRecv[0], &ch, 1);
  SCI0.SSR.BYTE &= ~0x40;
}


#pragma interrupt
void rxi1(void)
{
  char ch = SCI1.RDR;
  ring_write(&RingRecv[1], &ch, 1);
  SCI1.SSR.BYTE &= ~0x40;
}


#pragma interrupt
void txi0(void)
{
  char ch;

  if (ring_read(&RingSend[0], &ch, 1) <= 0) {
    SCI0.SCR.BYTE &= ~0x80;     // 送信終了。以後の送信割り込みを禁止
  } else {
    SCI0.TDR = ch;
    SCI0.SSR.BYTE &= ~0x80;
  }
}


#pragma interrupt
void txi1(void)
{
  char ch;

  if (ring_read(&RingSend[1], &ch, 1) <= 0) {
    SCI1.SCR.BYTE &= ~0x80;     // 送信終了。以後の送信割り込みを禁止
  } else {
    SCI1.TDR = ch;
    SCI1.SSR.BYTE &= ~0x80;
  }
}


#pragma interrupt
void tei0(void)
{
}


#pragma interrupt
void tei1(void)
{
}

#else


int sci_write(sci_port port, const char* data, int size)
{
  int i;
  for (i = 0; i < size; ++i) {
    if (port == SCI_0) {
      while (! (SCI0.SSR.BYTE & 0x80))
        ;
      SCI0.TDR = data[i];
      SCI0.SSR.BYTE &= ~0x80;

    } else if (port == SCI_1) {
      while (! (SCI1.SSR.BYTE & 0x80))
        ;
      SCI1.TDR = data[i];
      SCI1.SSR.BYTE &= ~0x80;
    }
  }
  return i;
}


int sci_read(sci_port port, char *buffer, int size)
{
  int i;
  for (i = 0; i < size; ++i) {
    if (port == SCI_0) {
      while (1) {
        if (SCI0.SSR.BYTE & 0x38) {
          // 受信エラー
          SCI0.SSR.BYTE &= ~0x38;
          break;

        } else if (SCI0.SSR.BYTE & 0x40) {
          buffer[i] = SCI0.RDR;
          SCI0.SSR.BYTE &= ~0x40;
          break;
        }
      }
    } else if (port == SCI_1) {
      while (1) {
        if (SCI1.SSR.BYTE & 0x38) {
          // 受信エラー
          SCI1.SSR.BYTE &= ~0x38;
          break;

        } else if (SCI1.SSR.BYTE & 0x40) {
          buffer[i] = SCI1.RDR;
          SCI1.SSR.BYTE &= ~0x40;
          break;
        }
      }
    }
  }
  return i;
}


int sci_readable(sci_port port)
{
  if (port == SCI_0) {
    return (SCI0.SSR.BYTE & 0x40) ? 1 : 0;

  } else if (port == SCI_1) {
    return (SCI1.SSR.BYTE & 0x40) ? 1 : 0;
  }
  return 0;
}


void sci_stop(sci_port port)
{
  (void)port;
  // !!!
}


void sci_flush(sci_port port)
{
  (void)port;
  // !!!
}
#endif
