/******************************************************************************
 *
 * $RCSfile: $
 * $Revision: $
 *
 * This module provides interface routines to the LPC ARM UARTs.
 * Copyright 2004, R O SoftWare
 * No guarantees, warrantees, or promises, implied or otherwise.
 * May be used for hobby or commercial purposes provided copyright
 * notice remains intact.
 *
 * Modified by Martin Thomas for LPC23xx 24xx
 *****************************************************************************/
#include "app_types.h"
#include "LPC_REGS.h"
#include "sys_config.h"

#include "uart2.h"

#ifndef USHRT_MAX
#define USHRT_MAX 16
#endif

#include "armVIC.h"
#include "uart2ISR.h"

#define U2_TX_PINSEL_REG    PINSEL4
#define U2_TX_PINSEL       (2UL<<16)  /* PINSEL4 Value for UART2 TX */
#define U2_TX_PINMASK      (3UL<<16)  /* PINSEL4 Mask  for UART2 RX */
#define U2_RX_PINSEL_REG    PINSEL4
#define U2_RX_PINSEL       (2UL<<18)  /* PINSEL4 Value for UART2 TX */
#define U2_RX_PINMASK      (3UL<<18)  /* PINSEL4 Mask  for UART2 RX */

uint8_t           uart2_rx_buffer[UART2_RX_BUFFER_SIZE];
uint16_t          uart2_rx_insert_idx, uart2_rx_extract_idx;
uint8_t           uart2_tx_buffer[UART2_TX_BUFFER_SIZE];
uint16_t          uart2_tx_insert_idx, uart2_tx_extract_idx;
int               uart2_tx_running;
t_uart2_txcomplete   uart2_tx_complete_callback;

void uart2TxFinished(void);

void uart2Init(uint16_t baud, uint8_t mode, uint8_t fmode)
{
  PCONP |= BIT( 24 );  // power on UART2	
	
  // set port pins for UART2
  // mthomas PINSEL0 = (PINSEL0 & ~U0_PINMASK) | U0_PINSEL;
  // set port pins for UART2
  // PINSEL0 = (PINSEL0 & ~U0_PINMASK) | U0_PINSEL;
  U2_TX_PINSEL_REG = ( U2_TX_PINSEL_REG & ~U2_TX_PINMASK ) | U2_TX_PINSEL;
  U2_RX_PINSEL_REG = ( U2_RX_PINSEL_REG & ~U2_RX_PINMASK ) | U2_RX_PINSEL;

  U2IER = 0x00;                         // disable all interrupts
  U2IIR;                                // clear interrupt ID
  U2RBR;                                // clear receive register
  U2LSR;                                // clear line status register

  // Set CLOCK for UART2 device (4x)
  //PCLKSEL1 = 0x00010000;
  
  // Set CLOCK for UART2 device (div 2)
  PCLKSEL1 &= 0xFFFDFFFF;
  PCLKSEL1 |= 0x00020000;

  // Preset callback function to none
  uart2_tx_complete_callback = 0;
  
  // set the baudrate
  U2LCR = ULCR_DLAB_ENABLE;             // select divisor latches 
  U2DLL = (uint8_t)baud;                // set for baud low byte
  U2DLM = (uint8_t)(baud >> 8);         // set for baud high byte

  // set the number of characters and other
  // user specified operating parameters
  U2LCR = (mode & ~ULCR_DLAB_ENABLE);
  U2FCR = fmode;

  // initialize the interrupt vector
  VICIntSelect     &= ~VIC_CHAN_TO_MASK(VIC_CHAN_NUM_UART2);
  VICIntEnClr       = VIC_CHAN_TO_MASK(VIC_CHAN_NUM_UART2); 
  VICVectAddr28     = (uint32_t)uart2ISR;
  VICVectPriority28 = 0xF;
  VICIntEnable      = VIC_CHAN_TO_MASK(VIC_CHAN_NUM_UART2);

  // initialize the transmit data queue
  uart2_tx_extract_idx = uart2_tx_insert_idx = 0;
  uart2_tx_running = 0;

  // initialize the receive data queue
  uart2_rx_extract_idx = uart2_rx_insert_idx = 0;

  // enable receiver interrupts
  U2IER = UIER_RBR;
}

/******************************************************************************
 *
 * Function Name: uart2Putch()
 *
 * Description:  
 *    This function puts a character into the UART output queue for
 *    transmission.
 *
 * Calling Sequence: 
 *    character to be transmitted
 *
 * Returns:
 *    ch on success, -1 on error (queue full)
 *
 *****************************************************************************/
int uart2Putch(int ch)
{
  uint16_t temp;
  unsigned cpsr;

  temp = (uart2_tx_insert_idx + 1) % UART2_TX_BUFFER_SIZE;

  if (temp == uart2_tx_extract_idx)
    return -1;                          // no room

  cpsr = disableIRQ();                  // disable global interrupts
  U2IER &= ~UIER_THRE;                  // disable TX interrupts
  restoreIRQ(cpsr);                     // restore global interrupts

  // check if in process of sending data
  if (uart2_tx_running)
    {
    // add to queue
    uart2_tx_buffer[uart2_tx_insert_idx] = (uint8_t)ch;
    uart2_tx_insert_idx = temp;
    }
  else
    {
    // set running flag and write to output register
    uart2_tx_running = 1;
    U2THR = (uint8_t)ch;
    }

  cpsr = disableIRQ();                  // disable global interrupts
  U2IER |= UIER_THRE;                   // enable TX interrupts
  restoreIRQ(cpsr);                     // restore global interrupts

  return (uint8_t)ch;
}

/******************************************************************************
 *
 * Function Name: uart2Space()
 *
 * Description:  
 *    This function gets the available space in the transmit queue
 *
 * Calling Sequence: 
 *    void
 *
 * Returns:
 *    available space in the transmit queue
 *
 *****************************************************************************/
uint16_t uart2Space(void)
{
  int space;

  if ((space = (uart2_tx_extract_idx - uart2_tx_insert_idx)) <= 0)
    space += UART2_TX_BUFFER_SIZE;

  return (uint16_t)(space - 1);
}

/******************************************************************************
 *
 * Function Name: uart2Puts()
 *
 * Description:  
 *    This function writes a NULL terminated 'string' to the UART output
 *    queue, returning a pointer to the next character to be written.
 *
 * Calling Sequence: 
 *    address of the string
 *
 * Returns:
 *    a pointer to the next character to be written
 *    (\0 if full string is written)
 *
 *****************************************************************************/
const char *uart2Puts(const char *string)
{
  register char ch;

  while ((ch = *string) && (uart2Putch(ch) >= 0))
    string++;

  return string;
}

/******************************************************************************
 *
 * Function Name: uart2Write()
 *
 * Description:  
 *    This function writes 'count' characters from 'buffer' to the UART
 *    output queue.
 *
 * Calling Sequence: 
 *    
 *
 * Returns:
 *    0 on success, -1 if insufficient room, -2 on error
 *    NOTE: if insufficient room, no characters are written.
 *
 *****************************************************************************/
int uart2Write(const char *buffer, uint16_t count)
{
  if (count > uart2Space())
    return -1;

  while (count && (uart2Putch(*buffer++) >= 0))
    count--;

  return (count ? -2 : 0);
}

/******************************************************************************
 *
 * Function Name: uart2TxEmpty()
 *
 * Description:
 *    This function returns the status of the UART transmit data
 *    registers.
 *
 * Calling Sequence: 
 *    void
 *
 * Returns:
 *    FALSE - either the tx holding or shift register is not empty
 *   !FALSE - if both the tx holding & shift registers are empty
 *
 *****************************************************************************/
int uart2TxEmpty(void)
{
  return (U2LSR & (ULSR_THRE | ULSR_TEMT)) == (ULSR_THRE | ULSR_TEMT);
}

int uart2RxEmpty(void)
{
	  if (uart2_rx_insert_idx == uart2_rx_extract_idx) // check if character is available
	  {
		  return 1;
	  }
	  else
	  {
		  return 0;
	  }
}


/******************************************************************************
 *
 * Function Name: uart2TxFlush()
 *
 * Description:  
 *    This function removes all characters from the UART transmit queue
 *    (without transmitting them).
 *
 * Calling Sequence: 
 *    void
 *
 * Returns:
 *    void
 *
 *****************************************************************************/
void uart2TxFlush(void)
{
  unsigned cpsr;

  U2FCR |= UFCR_TX_FIFO_RESET;          // clear the TX fifo

  // "Empty" the transmit buffer.
  cpsr = disableIRQ();                  // disable global interrupts
  U2IER &= ~UIER_THRE;                 // disable TX interrupts
  restoreIRQ(cpsr);                     // restore global interrupts
  uart2_tx_insert_idx = uart2_tx_extract_idx = 0;
}

/******************************************************************************
 *
 * Function Name: uart2Getch()
 *
 * Description:  
 *    This function gets a character from the UART receive queue
 *
 * Calling Sequence: 
 *    void
 *
 * Returns:
 *    character on success, -1 if no character is available
 *
 *****************************************************************************/
int uart2Getch(void)
{
  uint8_t ch;

  if (uart2_rx_insert_idx == uart2_rx_extract_idx) // check if character is available
    return -1;

  ch = uart2_rx_buffer[uart2_rx_extract_idx++]; // get character, bump pointer
  uart2_rx_extract_idx %= UART2_RX_BUFFER_SIZE; // limit the pointer
  return ch;
}

void uart2SubscribeTxFinished( t_uart2_txcomplete callback )
{
    uart2_tx_complete_callback = callback;
}

void uart2UnsubsribeTxFinished( t_uart2_txcomplete callback )
{
    uart2_tx_complete_callback = 0;
}


void uart2TxFinished(void)
{
    if (uart2_tx_complete_callback != 0)
        {
        uart2_tx_complete_callback();
        }
}


