
/*
 *
 *     Module Name: simpleSerial.c
 *	       Version: 1.00
 *   Original Date: 02/24/03	
 *	        Author: Li Zhen
 *	      Language: Ansi C
 * Compile Options:
 * Compile defines:
 *	     Libraries:
 *    Link Options:
 *
 *    Entry Points:  
 *
 *  Copyright (c) Digi International Inc., All Rights Reserved
 *  
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *  
 *  Restricted Rights Legend
 * 
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 * 
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * Description.
 * =======================================================================
 *  This file contains the code that sets up a simple serial driver to be 
 *  able to output some debugging informations in BSP initilization stage 
 *  before kernel is loaded.
 *  THE CODE IN THIS FILE MUST BE CUSTOMIZED TO SUPPORT YOUR APPLICATION
 *  HARDWARE.  The default implementation supports the NET+OS development
 *  boards, serial PORTA.
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *
 *  JZW    05/20/03  Include string.h and sysClock.h to pick up functional
 *                   prototypes
 *  JZW    10/24/05  In the function get_baud_div, the tables entitled
 *         Clocks and Clockmodes, were set up in memory. The problem is that
 *         when using simpleserial at very early times, memory may not be totally
 *         set up. This was causing these tables to be loaded with garbage. Due to this
 *         the loops in setupSimpleSerial, when used very early, were continuing
 *         without bounds. This is because of the way memory was at that time, 
 *         there were no 0,0 entries in the tables and so the loop exit criterion would
 *         never be hit. Fix is to set the tables up as const
 *  JZW    10/25/05 re previous fix, gcc requires static const as opposed to const static
 *         GH will take either. So changed declarations of Clocks and Clockmodes to
 *         static const
 *  JZW	   11/1/05 In function bsp_printf if the channel is not enabled, call setupSimpleSerial
 *         before continuing with sending characters over the serial channel
 */
#include <errno.h>
#include <string.h>
#include <gpio.h>
#include "sysClock.h"
#include <reg_def.h>
#include <ncc_init.h>
#include <gpio_def.h>
// #include "bsp.h"
#include "NA_ISR.h"
#include "netosIo.h"
#include <simpleserial.h>
#include "fwtypes.h"

#include "appconf_api.h"
#include "armgdb.h"
#include "ddi.h"

/*** Serial Channel Bit-Rate Register ***/
/* Control section ... */
#define BIT_GEN_ON             0x80000000U  /* Enable Bit-Rate Generator          */
#define BIT_RATE_X1            0x40000000U  /* Timing mode: 0 = X16 1 = X1        */
#define BIT_RATE_X16           0x00000000U  /* Timing mode: 0 = X16 1 = X1        */
#define BIT_TDCR_RDCR_X1       0x00000000U  /* for 20UM only. Transmit/Receive Divide Clock Rate, D20:19/D17:16=00/00 1X clock mode*/
#define BIT_TDCR_RDCR_X8	   0x00090000U  /* for 20UM only. Transmit/Receive Divide Clock Rate, D20:19/D17:16=01/01 8X clock mode*/
#define BIT_TDCR_RDCR_X16      0x00120000U  /* for 20UM only. Transmit/Receive Divide Clock Rate, D20:19/D17:16=10/10 16X clock mode*/
#define BIT_TDCR_RDCR_X32      0x001b0000U  /* for 20UM only. Transmit/Receive Divide Clock Rate, D20:19/D17:16=11/11 32X clock mode*/
#define RECV_CLK_SRC_EXT       0x20000000U  /* Receive clock source is External   */
#define RECV_CLK_SRC_INT       0x00000000U  /* Receive clock source is Internal   */
#define XMIT_CLK_SRC_EXT       0x10000000U  /* Transmit clock source is External  */
#define XMIT_CLK_SRC_INT       0x00000000U  /* Transmit clock source is Internal  */
#define RECV_CLK_OUT_ON        0x08000000U  /* Drive RXCLK out OUT1 of Port A/B   */
#define RECV_CLK_OUT_OFF       0x00000000U  /* Do not drive RXCLK to OUT1         */
#define XMIT_CLK_OUT_ON        0x04000000U  /* Drive TXCLK out OUT2 of Port C     */
#define XMIT_CLK_OUT_OFF       0x00000000U  /* Do not drive TXCLK to OUT2         */
#define BIT_RATE_CLK_EXT       0x00000000U  /* Use the external Clk source        */
#define BIT_RATE_CLK_SYS       0x01000000U  /* Use System Clk as source Bit-Rate  */
#define BIT_RATE_CLK_OUT1A     0x02000000U  /* Use OUT1A as source Bit-Rate Clk   */
#define BIT_RATE_CLK_OUT2A     0x03000000U  /* Use OUT2A as source Bit-Rate Clk   */
#define BIT_RATE_TXCINV        0x00800000U  /* TXD driven on rising edge of TX clock  */
#define BIT_RATE_RXCINV        0x00400000U  /* RRXD sampled on falling edge of RX clock */
#define BIT_RATE_CLKINV        0x00200000U  /* Receive Clock Invert               */

#define BIT_RATE_RX_DPLL       0x00001000U /* Recover clock from receive data */
/* Bits 23 to 11 are reserved */
/* Divide Rate section */
/* algorithms changed 6/26/00 multiply the baud rate * 10 by 16. Divide the XTAL_FREQ
 * by this product and then as the LAST step, subtract one from this quotient.
 */
#define BIT_RATE_DIV_MASK      0x000007FFU  /* Bit-Rate (baud) divisor count mask */
#define MIN_BAUD_RATE           75
#define MAX_BAUD_RATE           115200
#define DEFAULT_BAUD_RATE       9600  /* default used in case of error */
#define DEFAULT_BAUD_RATE_ERROR 5       /* 1 percent error */

/*** End Serial Channel Bit-Rate Register ***/
#define IVEC_TO_INUM(intVec)    ((int) (intVec))
#define INUM_TO_IVEC(intNum)    ((VOIDFUNCPTR *) (intNum))

#define SER1RX_INT_LVL      15    /* serial port interrupt level for each serial interrupt */
#define SER1TX_INT_LVL      14
#define SER2RX_INT_LVL      13
#define SER2TX_INT_LVL      12

#define INT_VEC_SERIAL1_RX        IVEC_TO_INUM(SER1RX_INT_LVL)  /* serial 1 receive interrupt */
#define INT_VEC_SERIAL1_TX        IVEC_TO_INUM(SER1TX_INT_LVL)  /* serial 1 transmit interrupt */
#define INT_VEC_SERIAL2_RX        IVEC_TO_INUM(SER2RX_INT_LVL)  /* serial 2 receive interrupt */
#define INT_VEC_SERIAL2_TX        IVEC_TO_INUM(SER2TX_INT_LVL)  /* serial 2 transmit interrupt */

#define NA_GDB_SERIAL_BAUD_RATE  115200

#define SIMPLESERIAL_ENABLE		0x80000000
#define SIMPLESERIAL_8BITS		0x03000000
#define SIMPLESERIAL_DTR		0x00020000
#define SIMPLESERIAL_RTS		0x00010000
#define SIMPLESERIAL_RCGT		0x04000000
#define SIMPLESERIAL_CLEAN		0xffffffff
#define SIMPLESSERIAL_GAPTIMER  0x000000ff
#define SIMPLESERIAL_BAUDRATE   9600

#define SIMPLESERIAL_RIE        0xA00

enum {
    NASS_CRA_ADDR,
    NASS_CRB_ADDR,
    NASS_RCT_ADDR,
    NASS_BRG_ADDR,
    NASS_SRA_ADDR,
    NASS_FIFO_ADDR,
    NASS_PORT_ADDR,
    BUS_UTILITY_SERIAL_be
};

struct baudTable {
    unsigned int value;
    unsigned int divr_or;
};

enum baudClkIdx {
    XTAL_IDX,
    SYS_IDX,
};

static const struct baudTable Clocks[] = {
    { 1, BIT_RATE_CLK_EXT }, /* XTAL_IDX */
    { 1, BIT_RATE_CLK_SYS }, /* SYS_IDX */
    {0,0},
};

static const struct baudTable Clockmodes[] = {
    {16, BIT_RATE_X16},
    {8, BIT_RATE_X1 | BIT_TDCR_RDCR_X8 | BIT_RATE_RX_DPLL},
    {32, BIT_RATE_X1 | BIT_TDCR_RDCR_X32 | BIT_RATE_RX_DPLL},
    {0,0},
};


static char fifo_buf[1024];         /* global buffer for data read from the receive line */
static unsigned int fifo_size = 0;  /* total number of data in fifo_buf */
static unsigned int fifo_index = 0; /* index on current byte in fifo_buf */
int          gdbSerialChannel = -1;
static int          gdbSerialConsole_mode = 0;

static int get_baud_div(int baudrate, uint32 *divr)
{
    const unsigned int ELEVEN_BIT_MAX = 2047;
    unsigned int Clk, bestClkIdx = 0;
    unsigned int clockMode, bestModeIdx = 0;
    unsigned int err, bestErr = 0xffffffff;
    unsigned int N, bestN = ELEVEN_BIT_MAX + 1;
    int clkindex = 0;
    int modeindex = 0;


    /* Iterate through clocks */
    while (Clocks[clkindex].value)  {
        switch(clkindex) {
        case XTAL_IDX:
            Clk = NAgetSysOscFreq();
            break;
        case SYS_IDX:
            Clk = NAgetCpuClkFreq();
            break;
        default:
            return 100; /* 100% error */
        };
        
        modeindex = 0;
        /* Iterate through divisors */
        while ( (clockMode = Clockmodes[modeindex].value) ) {

            /* Compute N */
            N = Clk / (clockMode * 2 * baudrate) - 1;

            /* Round up? */
            if ( (Clk % (clockMode * 2 * baudrate)) 
                 > (clockMode * baudrate)) 
            {
                /* remainder is more than half */
                N++;
            }

            /* Compute err */
            if (N > ELEVEN_BIT_MAX) {
                err = 0xffffffff;
            } else {
                unsigned int realBaud = Clk / (clockMode * 2 * (N + 1)) ;
                if ((int)realBaud > baudrate) {
                    err = realBaud - baudrate;
                } else {
                    err = baudrate - realBaud;
                }
            }

            if (err < bestErr) {
                bestN = N;
                bestErr = err;
                bestModeIdx = modeindex;
                bestClkIdx = clkindex;
            }

            if (bestErr == 0) {
                break;
            }
            modeindex++;
        }

        if (bestErr == 0) {
            break;
        }
        clkindex++;
    }

    if (bestN == ELEVEN_BIT_MAX + 1) {
        return 100;
    }

    if (bestErr == 0xffffffff) {
        return 100;
    }

    *divr = Clocks[bestClkIdx].divr_or 
        | Clockmodes[bestModeIdx].divr_or
        | bestN;

    return bestErr * 100 / baudrate;
    
}

static unsigned long get_register_addr(int channel, int addr_index)
{
    unsigned long addr = 0;
    
    if (channel == 0) /* BSP_SIMPLE_SERIAL_ON_PORTA */
    {
        switch (addr_index)
        {
            case NASS_CRA_ADDR:
                addr = (unsigned long)NARM_SC1CRA_ADDR;
                break;
                
            case NASS_CRB_ADDR:           
                addr = (unsigned long)NARM_SC1CRB_ADDR;
                break;
            case NASS_RCT_ADDR:           
                addr = (unsigned long)NARM_SC1RCT_ADDR;
                break;
            case NASS_BRG_ADDR:           
                addr = (unsigned long)NARM_SC1BRG_ADDR;
                break;
            case NASS_SRA_ADDR:           
                addr = (unsigned long)NARM_SC1SRA_ADDR;
                break;
            case NASS_FIFO_ADDR:          
                addr = (unsigned long)NARM_SC1FIFO_ADDR;
                break;
            case NASS_PORT_ADDR:
                addr = (unsigned long)NARM_PORTA_ADDR;
                break;
            
        }
    }
    else if (channel == 1) /* BSP_SIMPLE_SERIAL_ON_PORTB */
    {
        switch (addr_index)
        {
            case NASS_CRA_ADDR:
                addr = ( unsigned long)NARM_SC2CRA_ADDR;
                break;
            case NASS_CRB_ADDR:           
                addr = ( unsigned long)NARM_SC2CRB_ADDR;
                break;
            case NASS_RCT_ADDR:           
                addr = ( unsigned long)NARM_SC2RCT_ADDR;
                break;
            case NASS_BRG_ADDR:           
                addr = ( unsigned long)NARM_SC2BRG_ADDR;
                break;
            case NASS_SRA_ADDR:           
                addr = ( unsigned long)NARM_SC2SRA_ADDR;
                break;
            case NASS_FIFO_ADDR:          
                addr = ( unsigned long)NARM_SC2FIFO_ADDR;
                break;
            case NASS_PORT_ADDR:
                addr = (unsigned long)NARM_PORTB_ADDR;
                break;
            
        }
    }

    return addr;
}    

BOOLEAN is_serial_initialized(int channel)
{
    volatile unsigned long *reg_addr;    

    reg_addr = (volatile unsigned long *) get_register_addr(channel, NASS_CRA_ADDR);
    
    return (narm_read_reg(NARM_SCRA_REG, reg_addr, ce) != 0);
}    

int naSerial_isRxReady(int channel)
{
    volatile unsigned long * reg_addr;    
    int  rxrdy = 0;  
     
    reg_addr = (volatile unsigned long *) get_register_addr(channel, NASS_SRA_ADDR);
    rxrdy = narm_read_reg(NARM_SSRA_REG, reg_addr, rxrdy); 
    
    return (rxrdy);
}
/*  Function: setupSimpleSerial (void)
 *
 *  Description:
 *     This function will setup a Serial Port so BSP can send some info
 *     through the PORTA debugger port.
 *
 *  Parameters:
 *     none
 *  
 *  Return Value:
 *     none
 *
 */

void naSerial_Init(int channel, int baud)
{
    uint32 baud_div = 0;
    int error_rate;
    volatile unsigned long  *reg_addr;

    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_CRA_ADDR);

    /* setup serial port control register A */
    *reg_addr = SIMPLESERIAL_ENABLE | SIMPLESERIAL_8BITS | SIMPLESERIAL_DTR | SIMPLESERIAL_RTS | SIMPLESERIAL_RIE;
	
    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_CRB_ADDR);
	/*setup serial port A control register B*/
	*reg_addr = SIMPLESERIAL_RCGT;
	
    
    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_SRA_ADDR);
	/*setup serial port A status register*/
	*reg_addr = SIMPLESERIAL_CLEAN;  

    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_PORT_ADDR);
	*reg_addr |= GPIO_FULL_FUNCTION_SERIAL_PORT;
 
	/*setup serial port A receive charactor gap timer register*/
    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_RCT_ADDR);
	*reg_addr = SIMPLESERIAL_ENABLE | SIMPLESSERIAL_GAPTIMER;  

	/*setup serial port A bit-rate register, 9600 baudrate */

    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_BRG_ADDR);
    error_rate = get_baud_div(baud, &baud_div); 
    if (error_rate < DEFAULT_BAUD_RATE_ERROR) /* less than 5% */
        *reg_addr = SIMPLESERIAL_ENABLE | baud_div;
    else	
	    *reg_addr = SIMPLESERIAL_ENABLE | (NAgetSysOscFreq()/(2*16*baud) -1);  
}

/*  Function: bsp_fputc (char out)
 *
 *  Description:
 *     This function will print one byte infomation
 *     through the PORTA debugger port.
 *
 *  Parameters:
 *     out:		byte to be printed
 *  
 *  Return Value:
 *     none
 *
 */
void naSerial_putc(int channel, char out)
{
    volatile unsigned long * reg_addr;

	/* Wait for the Transmit Buffer Ready */
    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_SRA_ADDR);
	while (narm_read_reg(NARM_SSRA_REG, reg_addr, txrdy)== 0);

	/* Write the output byte */
    reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_FIFO_ADDR);
	*(volatile BYTE *)reg_addr = out;

	if (out == 0x0a)  /* If line feed, add carriage return */
	{
		/* Wait for the Transmit Buffer Ready */
        reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_SRA_ADDR);
		while (narm_read_reg(NARM_SSRA_REG, reg_addr, txrdy)== 0);

		/* Write the carriage return byte */
        reg_addr = (volatile unsigned long *)get_register_addr(channel, NASS_FIFO_ADDR);
		*(volatile BYTE *)reg_addr = 0x0d;
	}

}

/*  Function: bsp_fgetw (WORD32 *data)
 *
 *  Description:
 *     This function will print up to 4 bytes of data
 *     through the PORTA debugger port.
 *
 *  Parameters:
 *     data:        pointer to the location to store up to 4 bytes of data
 *  
 *  Return Value:
 *     size:        size of the data
 *
 */
int naSerial_get(int channel, char * data, WORD32 * length) 
{
    volatile unsigned long * reg_addr;    
    volatile unsigned long * fifo_addr;    
    
    unsigned long   invalue;
    char            * inchar;
    char            * invaluep;
    int             rxbc, rxrdy;  
    unsigned        data_avail;
    unsigned long   total_size = 0;
    int             rc = 0;
    
#ifdef _DEBUG_PRINTF_RX       
    char            *inbyte;
#endif   
    unsigned int    i;

    reg_addr = (volatile unsigned long *) get_register_addr(channel, NASS_SRA_ADDR);
    fifo_addr = (volatile unsigned long *) get_register_addr(channel, NASS_FIFO_ADDR);
    /* read until RBC */        
    do
    {
#ifdef _DEBUG_PRINTF_RX 
        if (narm_read_reg(NARM_SSRA_REG, reg_addr, rxover) == 1)
        {
            /* Todo: do we need to recover for this error or it's ok 
             * to continue?
             */
            bsp_printf("\noverrun\n");
            rc = -1;
        }
#endif                

        rxbc = narm_read_reg(NARM_SSRA_REG, reg_addr, rxbc);
        if (rxbc == 1) 
        {
        	narm_write_reg(NARM_SSRA_REG, reg_addr, rxbc,1);
            /* break;*/
        }

        /* read the receive line ready for incoming data */
        rxrdy = narm_read_reg(NARM_SSRA_REG, reg_addr, rxrdy); 
        if (rxrdy == 1)
        {
            /* RDY bit is set, determine number of valid bytes in FIFO Data Register */
            data_avail = narm_read_reg( NARM_SSRA_REG, reg_addr, rxfdb );
           
            if (data_avail == 0)
            {
                /* Receive a complete word; set the size to 4 bytes */
                data_avail = sizeof(WORD32);
/*                rc = -2; */
            }
           
#ifdef _DEBUG_PRINTF_RX  
            bsp_printf("- size -");
            bsp_fputc((char)(data_avail + 0x30));
            bsp_printf("\n");
#endif
            /* make sure we still have room in the data ptr */
            if (*length < (total_size + data_avail))
            {
                data_avail = *length - total_size;
            }
                                
            /* Read the data register */
/*            inchar = (unsigned long *)(data + total_size); */
            inchar = (char *)data;
            invalue = * (volatile unsigned long *)fifo_addr;

            invaluep = (char *)&invalue;
            inchar += total_size;

            for (i=0; i < data_avail; i++)
            {
                inchar[i] = invaluep[i];
            }
            /* let's append the data in the fifo_buf buffer */
            total_size += data_avail;
#ifdef _DEBUG_PRINTF_RX 
           /* debug - print out the data read from the 
            * receive line, all 4 bytes of data in the 
            * receive line, & the whole fifo_buf 
            */
           inbyte = (char *)inchar;
           for (i=0; i < data_avail; i++)
               bsp_fputc(inbyte[i]);
               
           inbyte = (char *)inchar;
           bsp_printf(" [");
           for (i=0; i<4; i++)
           {
                if (inbyte[i] != 0)
                   bsp_fputc(inbyte[i]);
                else
                   bsp_fputc('-');
           }     
           bsp_printf("] (");
           
           for (i=0; i<total_size; i++)
               bsp_fputc(data[i]);
           bsp_printf(")\n");
#endif               
        }
        
    } while(rxbc == 0 && total_size < *length);

    *length = total_size;
    
    return rc;
}
/*ad*
 * Initializes GDB's serial port.
 * This function initializes the serial port defined in APP_GDB_PORT with
 * NA_GDB_BAUDRATE baud rate.
 *
 * @external
 * @category  gdbserver:gdbserial
 * @since 6.3
 * @platform ns9360_a,connectcore9c_a
 * @include "armgdb.h"
 *
 * @note User must responsible for setting GPIO correctly.
 *
*ad*/
void naGdbSerialInit(void)
{
    naSerial_Init(gdbSerialChannel, NA_GDB_SERIAL_BAUD_RATE);
}

/*ad*
 * Send a character out to the gdb serial port. 
 * This function waits for transmission line ready for sending 
 * a character to the gdb serial port. 
 *  
 * @external
 * @category  gdbserver:gdbserial
 * @since 6.3
 * @platform ns9360_a,connectcore9c_a
 * @include "armgdb.h"
 *
 * @param    c      contains character to be transmitted.
 *
 * @link naGdbSerialInit
 *
 * @note If the gdb serial port is not initialized, it initializs the port before
 * sending the character.
 *
*ad*/
void naGdbSerialPutChar(char c)
{
    if (gdbSerialChannel == -1)
    {
        naGdbSerialInit();
    }
    
    if (gdbSerialChannel >= 0)
    {
        naSerial_putc(gdbSerialChannel, c);
    }

}

/*ad*
 * Get a byte character from the gdb serial port. 
 * 
 * This function waits for the data available on the receive line. It reads
 * all the data on the receive line until the receive buffer line is closed (RBC).
 * The data is stored in a 1K size of buffer. Only one byte of character is 
 * returned from this function.
 *
 * @external
 * @category  gdbserver:gdbserial
 * @since 6.3
 * @platform ns9360_a,connectcore9c_a
 * @include "armgdb.h"
 *
 * @return  character   a byte of character read from the receive line.
 *
 * @link naGdbSerialInit
 * @link naGdbSerialPutChar 
 *
 * @note If the gdb serial port is not initialized, it initializs the port before
 * reading the data.
 *
 *
*ad*/
int naGdbSerialGetChar(void)
{
    char          *inbyte;

#if 0
    char        output[20];
    int i;
extern void u32T_to_hbuf( unsigned long , char * );
#endif

    if (gdbSerialChannel == -1)
    {
        naGdbSerialInit();
    }
    /* if no data in the global buffer, let's get it from the receive line */
    if (gdbSerialChannel >= 0 && fifo_size == 0)
    {    
        fifo_size = sizeof fifo_buf;
        fifo_index = 0;

        naSerial_get(gdbSerialChannel, (char *)fifo_buf, (WORD32 *)&fifo_size);
#if 0
        
        bsp_printf("\nserial:");
        u32T_to_hbuf((unsigned long)fifo_size, output );
        bsp_printf(output);
        bsp_printf("\n");
        for (i=0; i < (int) fifo_size; i++)
        {
            u32T_to_hbuf((unsigned long)fifo_buf[i], output );
            bsp_printf(output);
            bsp_printf(" ");
            
        }
        bsp_printf("\n");
#endif
    }

    /* return only a single byte of character */    
    if (fifo_size > 0)
    {
        inbyte = (char *)&fifo_buf[fifo_index];
        fifo_size--;
        fifo_index++;
        return (int) *inbyte;
    }
    
    return 0;
}

/*ad*
 * See whether data is available to be read.
 *
 * @external
 * @category  gdbserver:gdbserial
 * @since 6.3
 * @platform ns9360_a,connectcore9c_a
 * @include "armgdb.h"
 *                        
 * @return  0   No data
 * @return  1   data is waiting to be read
 *
 * @link naGdbSerialInit
 * @link naGdbSerialGetChar
 *
 * @note The serial port will be initialized if not initialized.
 * 
*ad*/
int naGdbSerialIsQueueEmpty(void)
{
    
    int  rxrdy = 0;  
     
    if (gdbSerialChannel == -1)
    {
        naGdbSerialInit();
    }
    
    if (gdbSerialChannel >= 0)
    {
        rxrdy = naSerial_isRxReady(gdbSerialChannel);
    }
    
    return (rxrdy == 0);
}

/*ad*
 * Set/enable interrupt for gdb serial.
 * This is useful when gdb debugger sends a command to the unit
 * when it's running. 
 *
 * @external
 * @category  gdbserver:gdbserial
 * @since 6.3 
 * @include "armgdb.h"
 *
 * @param  isrHandler  pointer to a interrupt function to process 
 *                     commands sent by the debugger.
 *
 * @link naGdbInterrupt    
 *ad*/
void naGdbSerialSetupIrq(void * isrHandler)
{
    int irq_id;
        
    switch (gdbSerialChannel)
    {
        case 0: /* BSP_SIMPLE_SERIAL_ON_PORTA */
            irq_id = INT_VEC_SERIAL1_RX;
            goto _setisr;
            
        case 1: /* BSP_SIMPLE_SERIAL_ON_PORTB */
            irq_id = INT_VEC_SERIAL2_RX;
_setisr:
            naIsrUninstall(irq_id);
            naIsrInstall (irq_id, (NA_ISR_HANDLER) isrHandler, (void*) 0); 
    }
    
    return;
    
}    

void naGdbSerialEnableIsr(void)
{
    int irq_id;
        
    switch (gdbSerialChannel)
    {
        case 0: /* BSP_SIMPLE_SERIAL_ON_PORTA */
            irq_id = SER1RX_INT_LVL;
            goto _cont;
            
        case 1: /* BSP_SIMPLE_SERIAL_ON_PORTB */
            irq_id = SER2RX_INT_LVL;
_cont:
            naInterruptEnable(irq_id);
            break;
    }
    return;
}
void naGdbSerialDisableIsr(void)
{
    int irq_id;
        
    switch (gdbSerialChannel)
    {
        case 0: /* BSP_SIMPLE_SERIAL_ON_PORTA */
            irq_id = SER1RX_INT_LVL;
            goto _cont;
            
        case 1: /* BSP_SIMPLE_SERIAL_ON_PORTB */
            irq_id = SER2RX_INT_LVL;
_cont:
            naInterruptDisable(irq_id);
            break;
            
    }
    
    return;
    
}    

#define _NO_ERROR 0

int naGdbSerialConsoleIoctl(int channel, int request, char *arg)
{
    int rc = EBADF;

    NA_UNUSED_PARAM(request);
    NA_UNUSED_PARAM(arg);

    if (gdbSerialChannel == channel)
    {
        rc = _NO_ERROR;
    }
    return rc;
}
int naGdbSerialConsoleClose(int channel)
{
    int rc = EBADF;
    if (gdbSerialChannel == channel)
    {
        rc = _NO_ERROR;
    }
    return rc;
}
int naGdbSerialConsoleOpen(int channel, unsigned flags)
{
    int rc = EBADF;

    extern NaStatus naGdbConsole_Init(void);

    NA_UNUSED_PARAM(flags);

    if (gdbSerialChannel == channel)
    {
        gdbSerialConsole_mode = (O_NONBLOCK & flags) ? 0 : 1;
        rc = _NO_ERROR;
    }
    return rc;
}

int naGdbSerialConsoleInput(int channel, void *buffer, int length, int *bytesRead)
{
    int rc = EBADF;
    int len = length;

    extern int naGdbConsole_input(void *, int *, int);

    if (gdbSerialChannel == channel)
    {
        /* TODO: non-block or block io */
        if (naGdbConsole_input(buffer,  &len, gdbSerialConsole_mode) == 0)
        {
            *bytesRead = len;
            rc = _NO_ERROR;
        }
    }

    return rc;    
}

int naGdbSerialConsoleOutput(int channel, void *buffer, int length, int *bytesWritten)
{
    int rc = EBADF;
    int len = length;

extern int naGdbConsole_output(void *, int *, int);

    if (gdbSerialChannel == channel)
    {
        if (naGdbConsole_output(buffer, &len, gdbSerialConsole_mode) == 0)
        {
            rc = _NO_ERROR;
            *bytesWritten = (int)len;
        }
    }

    return rc;    
}

NaGdbio_t * naGdbSerialInterface(char *port_name)
{
    int         i; 
    NaGdbio_t   *serialPtr = NULL;
    
    static NaGdbio_t gdbSerialInterface = {
        naGdbSerialInit,
        naGdbSerialPutChar,
        naGdbSerialGetChar,
        naGdbSerialIsQueueEmpty 
    };

    
#ifdef _DEBUG_PRINTF
    printf("GDB Serial device: %s\n", port_name);
#endif    
    if (port_name != NULL)
    {
        for (i=0; i < MAX_DEV; i++) 
        {
            
        #ifdef _DEBUG_PRINTF
            printf("Devices in the system: %s\n", deviceTable[i].name);
        #endif    
            if (strcmp(deviceTable[i].name, port_name) == 0) 
            {
                gdbSerialChannel = deviceTable[i].channel; 
                /* INSTALL_DEVICE(i,a,b,b1,c,d,e,f,g,h,j,k) */
                deviceTable[i].close = naGdbSerialConsoleClose;
                deviceTable[i].config = NULL;
                deviceTable[i].enter = NULL;
                deviceTable[i].init = NULL;
                deviceTable[i].ioctl = naGdbSerialConsoleIoctl;
                deviceTable[i].open = naGdbSerialConsoleOpen;
                deviceTable[i].read = naGdbSerialConsoleInput;
                deviceTable[i].write = naGdbSerialConsoleOutput;

                serialPtr = &gdbSerialInterface;
                break;
            }
        }
    }
    
    return serialPtr;
}

    
