/* ------------------------------------------------------------------------*/
/* COPYRIGHT (c) 1992, 1995, 1996 CONTEMPORARY CONTROL SYSTEMS, INC.       */
/* ------------------------------------------------------------------------*/
/* Project:       20051 DRIVER                                             */
/* Filename:      arc51.C                                                  */
/* Description:   MAIN ROUTINES TO USE ANY COM20051 BASED BOARD            */
/* Version:       2.21                                                     */
/* Author:        Mike Justice;              				   */
/* Edited:	  Jack Fried;						   */
/* History:       ORIGINAL 09/14/92  for com20020                          */
/*                11/22/95 Improved Interrupt handling                     */
/*                02/03/95 fixed problem w/writing command chainning       */
/*                04/23/96 added support for long packets                  */
/*                04/02/97 bnl code now works for com20051                 */
/*		  04/18/97 edited for backplanr, pp ,long packets 	   */
/* ------------------------------------------------------------------------*/
/*                                                                         */
/*       arc51_init                 initialize the hardware                */
/*       arc51_read                 read a packet                          */
/*       arc51_write                write a packet                         */
/*       arc51_status               status of node                         */
/*       arc51_exit                 reset and cleanup                      */
/*                                                                         */
/* ------------------------------------------------------------------------*/
#include <stdio.h>
#include "arc51.h"

/* recv,tx page defines */
#define ENABLE_PAGE0   0x84
#define ENABLE_PAGE1   0x8c

#define   DISABLE     {}
#define   ENABLE      {}


/* ------------------------------------------------------------------------- */
/* global variable definition                                                */
/* ------------------------------------------------------------------------- */
USIGN8  arc51_nodeid = 15;
USIGN16 arc51_port;

/* ------------------------------------------------------------------------- */
/* local variable definition                                                 */
/* ------------------------------------------------------------------------- */
static  USIGN8  i,j,k;
static  USIGN8 count[2];



USIGN8  arc51_stat;
USIGN8  arc51_page;
USIGN8  arc51_offset;
USIGN8  arc51_nbytes;
USIGN8  arc51_config;
USIGN8  arc51_diag;
USIGN8  arc51_in_page;
USIGN8  arc51_out_page;
USIGN8  arc51_cmd;
USIGN8  arc51_rx_flag;
USIGN8  arc51_write_ack;
USIGN8  arc51_write_abort;
USIGN8  arc51_buffer[512];

USIGN16 arc51_interrupt_reg;
USIGN16 arc51_status_reg;
USIGN16 arc51_command_reg;
USIGN16 arc51_diag_reg;
USIGN16 arc51_addr_hi_reg;
USIGN16 arc51_addr_low_reg;
USIGN16 arc51_packet_mem;
USIGN16 arc51_config_reg;
USIGN16 arc51_tentid_reg;
USIGN16 arc51_nextid_reg;
USIGN16 arc51_nodeid_reg;
USIGN16 arc51_setup_reg;

/* ------------------------------------------------------------------------- */
/* function prototypes */
/* ------------------------------------------------------------------------- */

/****************************************************************************
 * FUNCTION NAME: arc51_init
 *
 * DESCRIPTION  : initial the node and join the network
 *
 * RETURN VALUE : status
 ***************************************************************************/
USIGN8 arc51_init()
{
    USIGN8 status;
    int i,found;


    /* setup the location of the regs */
    map_reg = 0x0FF;
    arc51_port = 0;

    /* an 8 bit interface uses +1x, a 16 bit interface users +2x */
    arc51_interrupt_reg = arc51_port;
    arc51_status_reg = arc51_port;
    arc51_command_reg = arc51_port + 1;
    arc51_diag_reg = arc51_port + 1;
    arc51_addr_hi_reg = arc51_port + 2;
    arc51_addr_low_reg = arc51_port + 3;
    arc51_packet_mem = arc51_port + 4;
    arc51_config_reg = arc51_port + 6;
    arc51_tentid_reg = arc51_port + 7;
    arc51_nextid_reg = arc51_port + 7;
    arc51_nodeid_reg = arc51_port + 7;
    arc51_setup_reg = arc51_port + 7;

    /* configuration - network timeout slowest, enable tx */
    arc51_config = US78 | ~TXEN | CCHEN | BACKPLANE;
    /* output the configuration - join the network */
    OUTP(arc51_config_reg, arc51_config);
    for(i=1;i!=0;i++);
do
{
    /* set the page numbers */
    arc51_in_page = 0;
    arc51_out_page = 2;
    arc51_rx_flag = FALSE;

    /* setup to use node id register */
    arc51_config = (USIGN8)INP(arc51_config_reg);
    arc51_config = (USIGN8)((arc51_config & SUB_AD) | NODE_ID_REG);
    OUTP(arc51_config_reg, arc51_config);

    /* clear the POR and RECON status bits */
    arc51_cmd = CLEARFLAGS;
    OUTP(arc51_command_reg, arc51_cmd);

    /* determine the node id  */
    OUTP(arc51_nodeid_reg, arc51_nodeid);    
    found = 0;
    DELAY(200);    
    /* reset the 20020 chip - toggle it */
    arc51_config = (USIGN8)INP(arc51_config_reg);
    arc51_config = (USIGN8)(arc51_config | RESET);
    OUTP(arc51_config_reg, arc51_config);
    arc51_config = (USIGN8)(arc51_config & ~RESET);
    OUTP(arc51_config_reg, arc51_config);
    DELAY(200);    

    /* get the reset indicator it must be a 0xd1 */
    OUTP(arc51_addr_hi_reg,0xc0);
    OUTP(arc51_addr_low_reg,0);
    arc51_buffer[0] = (USIGN8)INP(arc51_packet_mem);
    arc51_buffer[1] = (USIGN8)INP(arc51_packet_mem);



	if(arc51_nodeid > 0xf0)
	  for(i=0;i<=15;i++)
	  {
	   arc51_get_check_status();
            if ((arc51_diag & 0x40) != 0)
	    {
              found = 1; 
	      arc51_nodeid--;	
	      break;
	    }
 	  }


        if(arc51_nodeid < 0xf0)
	  for(i=0;i<=15;i++)
	  {
	   arc51_get_check_status();
            if ((arc51_diag & 0x40) != 0)
	    {
              found = 1; 
	      arc51_nodeid = 0xff;
	      break;
	    }
 	  }
	




} while(found);

    if (arc51_buffer[0] != 0xD1) status = 0;
    else {
        /* mask interrupts */
        OUTP(arc51_interrupt_reg,0);

        /* disable tx,rx */
        OUTP(arc51_command_reg, DISABLETX);
        OUTP(arc51_command_reg, DISABLERX);

        /* clear the POR and RECON status bits */
        arc51_cmd = CLEARFLAGS;
        OUTP(arc51_command_reg, arc51_cmd);

        /* short or long packets */
        OUTP(arc51_command_reg, LONGPACKET);

        /* setup network speed - 2.5Mbps */
        arc51_config = (USIGN8)((arc51_config & SUB_AD) | SETUP_REG);
        OUTP(arc51_config_reg, arc51_config);
        OUTP(arc51_setup_reg, 0X80);

        /* configuration - network timeout slowest, enable tx */
        arc51_config = US78 | TXEN | CCHEN | BACKPLANE;
        /* output the configuration - join the network */
        OUTP(arc51_config_reg, arc51_config);

        /* enable two reads, w/broadcast */
        arc51_cmd = ENABLE_PAGE0;
        OUTP(arc51_command_reg,arc51_cmd);
        arc51_cmd = ENABLE_PAGE1;
        OUTP(arc51_command_reg,arc51_cmd);

        status = E_OK;
    }

    /* return 1 if bad initialization, or 0 if E_OK */
    return(status);
}

/****************************************************************************
 * FUNCTION NAME: arc51_read
 *
 * DESCRIPTION  : read a packet
 *
 * RETURN VALUE : status
 ***************************************************************************/
USIGN8 arc51_read(USIGN8 *data_ptr)
{
    USIGN8 status;
    USIGN16 bufcount,i;

        /* check the recieve (RIH) bit */
        arc51_get_check_status();
        status = arc51_stat;
        if ((status & RIH) == RIH) 
        {

             /*RESET COMMAND CHAINING */
	    OUTP(arc51_command_reg,0x08);
  	     
            /* enable the next read page */
            arc51_page = arc51_in_page;
            arc51_in_page++;
            arc51_in_page &= 1;
            status = E_OK;

            /* read the 1st 2 bytes sid,did */
            if (arc51_page == 0)
            {
                OUTP(arc51_addr_hi_reg,0xc0);
            }
            else
            {
                OUTP(arc51_addr_hi_reg,0xc2);
            }
            OUTP(arc51_addr_low_reg,0);
            *data_ptr = (USIGN8)INP(arc51_packet_mem);
            *(data_ptr+1) = (USIGN8)INP(arc51_packet_mem);

            /* read the packet size */
            count[0] = (USIGN8)INP(arc51_packet_mem);
            count[1] = (USIGN8)INP(arc51_packet_mem);

            /* store real count */

            /* long or short */
            if (count[0] == 0) 
            {
                arc51_offset = count[1];
                arc51_nbytes = (USIGN8)((~count[1])+1);
                *(data_ptr+3) = arc51_nbytes;
                *(data_ptr+2) = 1;
                bufcount = 256 + arc51_nbytes;
            }
            else
            {
                arc51_offset = count[0];
                arc51_nbytes = (USIGN8)((~count[0])+1);
                *(data_ptr+3) = arc51_nbytes;
                *(data_ptr+2) = 0;
                bufcount = arc51_nbytes;
            }

            /* read the next bytes */
            if (arc51_page == 0)
            {
                OUTP(arc51_addr_hi_reg,0xc0);
            }
            else
            {
                OUTP(arc51_addr_hi_reg,0xc2);
            }
            OUTP(arc51_addr_low_reg,arc51_offset);
            
            /* transfer the buffer to storage */
            for (i=0; i<bufcount; i++)
            {
                *(data_ptr+4+i) = (USIGN8)INP(arc51_packet_mem);
            }

            /* enable this page */
            if (arc51_in_page == 0) arc51_cmd = ENABLE_PAGE1;
            else arc51_cmd = ENABLE_PAGE0;
            OUTP(arc51_command_reg,arc51_cmd);
        }
        else
        {
            status = E_NO_PACKET;
        }

    /* return system status */
    return(status);
}

/****************************************************************************
 * FUNCTION NAME: arc51_write
 *
 * DESCRIPTION  : write a packet
 *
 * RETURN VALUE : status
 ***************************************************************************/
USIGN8 arc51_write(USIGN8 *data_ptr)
{
    USIGN8 page;
    USIGN8 count;
    USIGN8 lors;
    USIGN8 offset;
    USIGN8 status;
    USIGN8 counts[2];

    /* check if available */
    arc51_get_check_status();
    status = arc51_stat;
    if ((status & TA2) > 0) 
    {
        status = E_OK;
        page = arc51_out_page;

        /* SID and DID */
        arc51_write_data(page, 0, 2, 0, data_ptr);

        /* user packet format - SID, DID, msbcount, lsbcount, data,... */
        data_ptr += 2;

        /* determine the count and packet size to use */
        if (*data_ptr > 0) {

            /* long packet */
            lors = 1;
            data_ptr++;
            count = *data_ptr;
            if (count == 0) status = E_BAD_PACKET_SIZE;
            else {
                offset = (USIGN8)((~count)+1);
                counts[0] = 0;
                counts[1] = offset;
            }
        }
        else {
            /* short packet */
            lors = 0;
            data_ptr++;
            count = *data_ptr;
            if (count > 253) status = E_BAD_PACKET_SIZE;
            else {
                offset = (USIGN8)((~count)+1);
                counts[0] = offset;
            }
        } 

        /* load the buffer */
        if (status == E_OK) {

            /* load the count/fp */
            arc51_write_data(page, 2, 2, 0, &counts[0]);

            /* load the data */
            data_ptr++;
            arc51_write_data(page, offset, count, lors, data_ptr);

            /* reset ack flag */
            arc51_write_ack = FALSE;

            /* send it out */
            page <<= 3;
            page |= 3;
            OUTP(arc51_command_reg, page);

            /* incr page and check for rollover */
            arc51_out_page++;
            if (arc51_out_page > 3) arc51_out_page = 2;
        }
    }
    else status = E_TX_BUSY;

    /* return system status */
    return(status);
}

/****************************************************************************
 * FUNCTION NAME: arc51_status
 *
 * DESCRIPTION  : status of network
 *
 * RETURN VALUE : status
 ***************************************************************************/
USIGN8 arc51_status(void)
{
    /* get the status */
    arc51_get_check_status();

    /* return status */
    return(arc51_stat);
}


/****************************************************************************
 * FUNCTION NAME: arc51_write_data
 *
 * DESCRIPTION  : Write data from buffer location to a specific page
 *
 * RETURN VALUE : none
 ***************************************************************************/
void arc51_write_data(USIGN8 page, USIGN8 offset, USIGN8 count, USIGN8 shortlong, 
                USIGN8 *user_buffer)
{
    USIGN16 address;

    /* set the address pointers, autoincrement on write */
    address = (512 * page) + offset;
    OUTP(arc51_addr_hi_reg, (USIGN8)((address >> 8) | AUTOINC) & WRDATA);
    OUTP(arc51_addr_low_reg, (USIGN8)address);

    /* write the bytes to the page */
    while(count--) 
    {
        OUTP(arc51_packet_mem, *user_buffer++);
    }

    /* do it another 256x */
    if (shortlong == 1) {
        count = 0;
        do {
            OUTP(arc51_packet_mem, *user_buffer);
            user_buffer++;
            count--;
        } while (count != 0);
    }
}


/****************************************************************************
 * FUNCTION NAME: arc51_get_check_status
 *
 * DESCRIPTION  : get and check the device status
 *
 * RETURN VALUE : none
 ***************************************************************************/
void arc51_get_check_status(void)
{
    /* read the status */
    arc51_stat = (USIGN8)INP(arc51_status_reg);
    arc51_diag = (USIGN8)INP(arc51_diag_reg);

    /* check for TMA ack */
    if ((arc51_stat & TMA) == TMA) {
        arc51_write_ack = TRUE; 
    }

    /* check for MYRECON */
    if ((arc51_diag & MYRECON) == MYRECON) {

        /* clear flags */
        OUTP(arc51_command_reg, CLEARFLAGS);
        arc51_stat = (USIGN8)INP(arc51_status_reg);
    }

    /* check for RECON */
    if ((arc51_stat & RECON) == RECON) {

        /* clear flags */
        OUTP(arc51_command_reg, CLEARFLAGS);
        arc51_stat = (USIGN8)INP(arc51_status_reg);
    }

    /* check for POR flag */
    if ((arc51_stat & POR) == POR) {

        /* clear flags */
        OUTP(arc51_command_reg, CLEARPOR);
    }

    /* check for EXENAK */
    if ((arc51_diag & EXC_NAK) == EXC_NAK) {

        /* clear flags */
        OUTP(arc51_command_reg, CLEARPOR);

        /* kill the last transmit */
        OUTP(arc51_command_reg, DISABLETX);

        /* notify the write routine */
        arc51_write_abort = TRUE;
    }

    /* read the status again */
    arc51_stat = (USIGN8)INP(arc51_status_reg);
    arc51_diag = (USIGN8)INP(arc51_diag_reg);
}


/****************************************************************************
 * FUNCTION NAME: arc51_exit  
 *
 * DESCRIPTION  : turn off 20020 and disable interrupt if used
 *
 * RETURN VALUE : none
 ***************************************************************************/
VOID arc51_exit(void)
{
    /* disable the transmitter */
    OUTP(arc51_command_reg, DISABLETX);

    /* disable the reciever */
    OUTP(arc51_command_reg, DISABLERX);

    /* disable the tx enable */
    arc51_config &= (USIGN8)~TXEN;
    OUTP(arc51_config_reg, arc51_config);

  
}


/****************************************************************************
 * FUNCTION NAME: INP
 *
 * DESCRIPTION  : get data from memory location add	
 *
 * RETURN VALUE : data stored at add
 ***************************************************************************/
USIGN8 INP(USIGN8  add)
{
return (r51_read(add));
}


/****************************************************************************
 * FUNCTION NAME: arc51_get_check_status
 *
 * DESCRIPTION  : store data (val) at memory location add
 *
 * RETURN VALUE : none
 ***************************************************************************/
/* output function for the 8051 */
void OUTP(USIGN8 add,USIGN8 val)
{
 r51_write(add,val);
}


