/******************************************************************************
 *
 *                                     Sio_d
 *
 * This module contains the serial I/O driver of CMON. Since the operatin is
 * simmilar for all UARTS, this module is the same for all, and it includes
 * an appropriate definition file according to the UART type.
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Coby       | Written                                        |
 *   +----------+------------+------------------------------------------------+
 * 
 *****************************************************************************/


#include "cc_def.h"
#include "sio_d.h"
#include "io.h"

/*-----------------------------------------------------------------------------
 *                                 
 *                                 Variable definitions
 * 
 *---------------------------------------------------------------------------*/

/*
 * Bits in the communication byte, of the operation mode (OM) command.
 * They specify the offset in the nibble.
 */

#define HOST_ECHO_BIT    0
#define HOST_CRLF_BIT    1
#define CMON_ECHO_BIT    2
#define CMON_CRLF_BIT    3

/*
 * public variables.
 */

public int tr_buf_ptr;
public int xr_buf_ptr;

                                 /* Max time to wait for character read      */
#define IO_TIMEOUT  /*NOSTRICT*/ ((int) 0x90000)

/*
 * The following variable is used only by transparent mode functions.
 */

private int cur_sio_port;

private char  cur_ctrl_B, cur_ctrl_H, cur_ctrl_Q, cur_ctrl_S;

/* 
 * The folowing flags specify per board: if cmon should echo its input,
 * if cmon should translate CR to CRLF, if the host echoes its input,
 * if the host translates CR to CRLF.
 * The are taken from the communication byte of the OM command.
 */

private boolean  cmon_echo[2], cmon_crlf[2], host_echo[2], host_crlf[2];


/*
 * Include the appropriate header file
 */

#include SERIAL_FILE

/*
 * Define local functions
 */
private boolean sio_get_char(/* port, chp */);
private void sio_put_char (/* port, ch */);
#if (TRANSPARENT == TRUE)
private int do_transparent_communication();
#endif

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *              cub_decode()
 *
 * Init Uart decode function. This function accept the port # and the baud rate
 * and initializes this port to the specified baud rate
 *
 *---------------------------------------------------------------------------*/
public void cub_decode__(cmd, response)
str cmd, response;
{
int port, divisor, i, baud;
unsigned len;
s_str hex_port, hex_baud;

response[0] = '\0';

    /*
     * Get the port #
     */
    get_first_arg__(&cmd[3],  hex_port,  &len,  S_SIZE, 
                   /* Don't accept null arg */ TRUE);

    if (strlen(hex_port) > 1) {
        ERROR( e_syntax__);
    }
    switch (hex_port[0]) {
	case '0':
#ifdef UART_0_ADDR
		port = 0;
#else
		ERROR( e_syntax__);
#endif
		break;
	case '1':
#ifdef UART_1_ADDR
		port = 1;
#else
                ERROR( e_syntax__);
#endif
		break;
	default:
	   	ERROR( e_syntax__);
    }

    /*
     * Get the Uart divisor
     */
    get_first_arg__(&cmd[3 + len],  hex_baud,  &len,  S_SIZE, 
                        /* Don't allow_no_arg */ TRUE);

    hex_to_bin__( hex_baud,  &baud, 'D');
    for (i=0; uart_table[i].baud > 0; i++){
	if (uart_table[i].baud == baud) {
	    /*
	    write_message__(ready_prompt__);
	    DELAY(0x10000);
	    */
	    init_uart(port, uart_table[i].divisor, 0);
	    return;
	}
    }
    ERROR( e_syntax__);

}

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *              Init_sio__()
 *
 *
 *---------------------------------------------------------------------------*/
public void init_sio__()
{
int divisor;

    divisor = UART_DIVISOR;
#ifdef UART_0_ADDR
    init_uart(0, divisor, 0);	/* Init port 0 */
#endif
#ifdef UART_1_ADDR
    init_uart(1, divisor, 0);	/* Init port 1 */
#endif
#if (UART_TYPE == PC_EMUL)
    init_uart(0, divisor, 0);
#endif

}

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *              Set_sio_operation_mode(com_byte, b, h, q, s)
 *
 * This function is called every time the operation mode changes.
 * It updates the different communication mode variables.
 *
 *---------------------------------------------------------------------------*/

public void set_sio_operation_mode__(com_byte, b, h, q, s)
    byte  com_byte;
    char  b, h, q, s;
{


    cmon_echo[0] = BIT_ON((0 + CMON_ECHO_BIT), com_byte);
    cmon_crlf[0] = BIT_ON((0 + CMON_CRLF_BIT), com_byte);
    host_echo[0] = BIT_ON((0 + HOST_ECHO_BIT), com_byte);
    host_crlf[0] = BIT_ON((0 + HOST_CRLF_BIT), com_byte);
    cmon_echo[1] = BIT_ON((1 + CMON_ECHO_BIT), com_byte);
    cmon_crlf[1] = BIT_ON((1 + CMON_CRLF_BIT), com_byte);
    host_echo[1] = BIT_ON((1 + HOST_ECHO_BIT), com_byte);
    host_crlf[1] = BIT_ON((1 + HOST_CRLF_BIT), com_byte);

    cur_ctrl_B = b;
    cur_ctrl_H = h;
    cur_ctrl_Q = q;
    cur_ctrl_S = s;

} /* set_sio_operation_mode */

/*^L*/
/*----------------------------------------------------------------------------
 *
 *                       Sio_get_char (port, chp)
 *
 * This function checks if a character is ready on  the serial device.
 * If that is the case it reads it into chp and returns TRUE, otherwise
 * it returns FALSE.
 *
 * The function uses two macros:
 *                      READ_CHAR_IS_READY(port)
 *                      GET_CHAR(port)
 * This macros are defined in the device definition file.
 *
 *---------------------------------------------------------------------------*/

private boolean sio_get_char(port, chp)
           int     port;
           char    *chp;
{
    if (READ_CHAR_IS_READY(port)){
        *chp = GET_CHAR(port);
        return(TRUE);
    }
    return (FALSE);
}


/*----------------------------------------------------------------------------
 *
 *                      Sio_put_char (port, ch)
 *
 * This procedure waits until serial device is ready to accept charcter.
 * It then writes ch into the serial device.
 
 * The routine uses two macros:
 *                     READY_TO_WRITE_CHAR(port)
 *                     PUT_CHAR(port,ch)
 * This macros are defined in the device definition file.
 *
 *---------------------------------------------------------------------------*/
 
private void sio_put_char (port, ch)
          int     port;
          char    ch;
{
    while (NOT READY_TO_WRITE_CHAR(port))
        ;
    PUT_CHAR(port,ch);
}



#if (TRANSPARENT == TRUE)
/*----------------------------------------------------------------------------
 *
 *                 Do_transparent_communication()
 *
 * This function handles the transparent communication in transparent mode.
 * It alternatly trys to read charctrer from one of the port.
 * When one of the port did accept charcter the routine check should it write
 * this charctrer to the other port or is it a beginning of a monitor command.
 * In case that the charcter is beginning of monitor command then the function
 * exit returning the port number where the command should be read. 
 *
 *---------------------------------------------------------------------------*/

private int do_transparent_communication()
{
    int    port;
    char   ch;
    
    port = 0;
    while (1) {
        if (sio_get_char(port, &ch)){
            if (ch == cur_ctrl_B) {
                return (port);
            }
            sio_put_char(port ^ 1, ch);
        }
        port ^= 1;
    }
}
#endif
 
/*^L*/

/*----------------------------------------------------------------------------
 *
 *              Serial_read_line__(portp, line, len, time_out)
 *
 * This function reads ascii line from the given serial port.
 * Len is the maximum line length.
 * Time_out flags to activate the timeout mechanism.
 *
 * The function return when one of the following conditions is true, which ever
 * comes first:
 *             1) Read charcter is <CR> 
 *             2) Len charcter where read.
 *             3) Timeout was triggerd.
 *
 * The function returns the actual number of  charcter that were read.
 *---------------------------------------------------------------------------*/
 
public unsigned serial_read_line__(portp, line, len, time_out)
        int        *portp, len;
        str        line;
        boolean time_out;
{
    int            cur_char, time_count;
    char    ch;
    boolean success;

    if (*portp == CUR_PORT) {
#if (TRANSPARENT == TRUE)
        if (cur_op_mode__ == TRANSP) {
            *portp = do_transparent_communication();
            cur_sio_port = *portp;
        }
        else
#endif
	{
            *portp = 0;
        }
    }   
    for (cur_char = 0; cur_char < len; cur_char++){
        time_count = IO_TIMEOUT;
        do {
            success = sio_get_char (*portp, &ch);
            if (time_out AND (cur_char != 0) AND (time_count-- == 0)){
                ERROR( e_pol_timeout__);
            }
        } while (NOT success);
        /*
         * Check for special control character
         */
         
        if (ch == cur_ctrl_H) {
            if (cur_char > 0){
                cur_char -= 2;
                if (cmon_echo[*portp]) {
                    sio_put_char(*portp, ch);
                    sio_put_char(*portp, ' ');
                    sio_put_char(*portp, ch);
                }
            }
        }
        else if (ch == CR) {
	    line[cur_char] = CR;
            if (cmon_echo[*portp]) {
                sio_put_char(*portp, ch);
                if (cmon_crlf[*portp]) {
                    sio_put_char(*portp, LF);
                }
            }
            return (cur_char);
        }
        else {
            line[cur_char] = ch;
            if (cmon_echo[*portp]) {
                sio_put_char(*portp, ch);
            }
        }
    } /* for */
    return (cur_char);
}
  
/*^L*/

/*----------------------------------------------------------------------------
 *
 *              Serial_read_buffer__(portp, buf, len, time_out)
 *
 * This function reads binary data to buffer from the given serial port.
 * Len is the buffer length.
 * Time_out flags to activate the timeout mechanism.
 *
 * The function return when one of the following conditions is true, which ever
 * comes first:
 *             1) Len bytes where read.
 *             2) Timeout was triggerd.
 *
 * The function returns the actual number of bytes that were read.
 *---------------------------------------------------------------------------*/
 
public unsigned serial_read_buffer__(portp, buf, len, time_out)
        int        *portp, len;
        byte        *buf;
        boolean time_out;
{
    int            cur_byte, time_count;
    boolean success;
    char ch;

    if (*portp == CUR_PORT) {
#if (TRANSPARENT == TRUE)
        if (cur_op_mode__ == TRANSP) {
            *portp = do_transparent_communication();
            cur_sio_port = *portp;
        }
        else
#endif
	    {
            *portp = 0;
        }
    }   
    for (cur_byte = 0; cur_byte < len; cur_byte++){
        time_count = IO_TIMEOUT;
        do {
            success = sio_get_char (*portp, &ch);
            if (time_out  AND (cur_byte != 0) AND (time_count-- == 0)){
                ERROR( e_pol_timeout__);
            }
        } while (NOT success);

        buf[cur_byte] = ch;
        if (cmon_echo[*portp]) {
                sio_put_char(*portp, ch);
        	if (ch == CR && cmon_crlf[*portp]) {
			sio_put_char(*portp,  LF);
		}
        }

    } /* for */
    return (cur_byte);
}
  

/*^L*/      

/*-----------------------------------------------------------------------------
 *
 *                 serial_write_line__(port, line, len)
 *
 * This routine write a line to the given serial port.
 * The Actual number of byte that is writen is the minimum of len and the 
 * length of the line.
 * Return value: actual number of charcter writen.
 *---------------------------------------------------------------------------*/

public unsigned serial_write_line__(port, line, len)
        int        port, len;
        str        line;
{
    int            i;
    char    ch;
    
    if (port == CUR_PORT) {
        if (cur_op_mode__ == TRANSP) {
            port = cur_sio_port;
        }
        else{
            port = 0;
        }
    }   
    for (i = 0; i < len; i++){
        /*
         * Try to read from port ^S
         */
        if (sio_get_char(port, &ch) AND (ch == cur_ctrl_S)){
            while ( NOT(sio_get_char(port, &ch) AND (ch == cur_ctrl_Q)) );
        }
        /*
         * If the current charcter is null write CR and exit
         */
        if (line[i] == '\0') {
            sio_put_char (port, CR);
            if (host_echo[port]) {
                while (NOT sio_get_char(port, &ch));
            }
            if (cmon_crlf[port]) {
                sio_put_char(port, LF);
                if (host_echo[port]) {
                    while (NOT sio_get_char(port, &ch));
                }
            }
            return (i);
        }
        /*
         * Send the charcter to the port
         */
        sio_put_char(port, line[i]);
        if (host_echo[port]) {
            while (NOT sio_get_char(port, &ch));
        }
    }
    return (i);
}
 

/*-----------------------------------------------------------------------------
 *
 *                 serial_write_buffer__(port, buffer, len)
 *
 * This routine write a buffer to the given serial port.
 *---------------------------------------------------------------------------*/

public void serial_write_buffer__(port, buf, len)
        int        port, len;
        byte        *buf;
{
    int            i;
    char    ch;
    
    if (port == CUR_PORT) {
        if (cur_op_mode__ == TRANSP) {
            port = cur_sio_port;
        }
        else{
            port = 0;
        }
    }   
    for (i = 0; i < len; i++){
        /*
         * Try to read from port ^S
         */
        if (sio_get_char(port, &ch) AND (ch == cur_ctrl_S)){
            while ( NOT(sio_get_char(port, &ch) AND (ch == cur_ctrl_Q)) );
        }
        /*
         * Send the charcter to the port
         */
        sio_put_char(port, buf[i]);
        if (host_echo[port]) {
            while (NOT sio_get_char(port, &ch));
        }
	if (buf[i] == CR && cmon_crlf[port]) {
		sio_put_char(port, LF);
		if (host_echo[port]) {
			while (NOT sio_get_char(port,  &ch));
		}
	}
    }
}
