///////////////////////////////////////////////////////////////////////////////////
// File     : boot_tty_driver.c
// Date     : 18/01/2017
// Author   : Alain Greiner / Vu Son
// Copyright (c) UPMC-LIP6
///////////////////////////////////////////////////////////////////////////////////

#include <boot_config.h>
#include <boot_tty_driver.h>
#include <hal_types.h>
#include <boot_utils.h>

#ifndef SEG_TTY_BASE
# error "The SEG_TTY_BASE value should be defined in the 'boot_config.h' file"
#endif

#ifndef IO_CXY
# error "The IO_CXY value should be defined in the 'boot_config.h' file"
#endif


/****************************************************************************
 *                            Internal functions.                           *
 ****************************************************************************/

/****************************************************************************
 * This function returns the value of the a TTY register.                   *
 * @ reg    : TTY register to be read.                                      *
 * @ returns the value stored in 'reg'.                                     *
 ****************************************************************************/
static uint32_t boot_tty_get_register( uint32_t reg )
{
    cxy_t      cxy = IO_CXY;
    uint32_t * ptr = (uint32_t *)SEG_TTY_BASE + reg;
    
    return boot_remote_lw( XPTR( cxy , ptr ) ); 
    
} // boot_tty_get_register()

/****************************************************************************
 * This function sets a new value to a TTY register.                        *
 * @ reg    : TTY register to be configured.                                *
 * @ val    : new value to be written to 'reg'.                             *
 ****************************************************************************/
static void boot_tty_set_register( uint32_t reg, 
                                   uint32_t val )
{
    cxy_t      cxy = IO_CXY;
    uint32_t * ptr = (uint32_t *)SEG_TTY_BASE + reg;

    boot_remote_sw( XPTR( cxy , ptr ) , val ); 

} // boot_tty_set_register()

/****************************************************************************
 *                           Driver API functions.                          *
 ****************************************************************************/

int boot_tty_write( char    * buf,
                    uint32_t  nbytes )
{
    uint32_t nb_printed;    /* Iterator for printing loop.              */
    uint32_t nb_test;       /* Iterator for retry loop.                 */
    uint32_t error;         /* Used to detect if an error occurs.       */

    /* Printing to the boot TTY terminal. */
    for (nb_printed = 0; nb_printed < nbytes; nb_printed++)
    {
        // Polling the TTY driver status.
        if ((boot_tty_get_register(TTY_STATUS) & TTY_WRITE_BUSY))
        {
            // TTY_WRITE_BUSY bit of TTY_STATUS register is set, keeps polling.
            error = 1;
            for (nb_test = 0; nb_test < 10000; nb_test++)
                if ((boot_tty_get_register(TTY_STATUS) & TTY_WRITE_BUSY) == 0)
                {
                    error = 0;
                    break;
                }

            // Reporting an error if the TTY_WRITE_BUSY bit is still set after
            // 10000 retries.
            if (error)
                return -1;
        }

        // Writing a character to the boot TTY terminal. 
        // Special treatment for a newline: Carriage Return before Line Feed.
        if (buf[nb_printed] == '\n')
            boot_tty_set_register(TTY_WRITE, (uint32_t)'\r');
        boot_tty_set_register(TTY_WRITE, (uint32_t)buf[nb_printed]);
    }

    return 0;

} // boot_tty_write()
