/*********************************************************************** 
* NOTICE
* All files contained on this disk are subject to the licensing conditions
* issued by MOTOROLA Inc.
*
* All files are copyright 1993 by MOTOROLA Inc. 
************************************************************************/


/********************************************************
 * File:	ethernet.c
 *
 * Description:
 *	The driver routines for handling an ETHERNET port.
 *
 * Routines:
 *	ethernet_init
 *	ethernet_status
 *
 * Author:
 *	Jonathan Masel
 ********************************************************/

#include "gct.h"
#include "config.h"
#include "types.h"
#include "status.h"
#include "mtypes.h"
#include "msg.h"
#include "bss.h"
#include "quicc.h"
#include "states.h"
#include "ethernet.h"





/********************************************************
 * routine:	ethernet_init
 *
 * description:
 *	Initialize the driver tables for
 *	ETHERNET ports.
 *
 * arguments:
 *	n		the driver's child number
 *			(i.e. which port)
 *	scc		points to an scc configuration structure
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
ethernet_init(n, scc)
int n;
struct scc *scc;
{
	GCT *gct;
	int (* *protocol)();
	unsigned adr, rom;
	WORK_AREA *wa;
	extern int ethernet_txreq(), ethernet_rxfr(),
		ethernet_conf(), ethernet_txerr(),
		ethernet_busy(),
		return_to_pool(), ethernet_status(), ethernet_abort();

	GETGCT(gct);
	protocol = gct->driver->protocol;
	wa = (WORK_AREA *)gct->driver->child[n].work_area;
	rom = gct->driver->rom;

	adr = (unsigned)ethernet_txreq + rom;
	protocol[SUB(TX_REQ) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)ethernet_rxfr + rom;
	protocol[SUB(RX_IND) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)ethernet_conf + rom;
	protocol[SUB(TX_CONF) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)ethernet_txerr + rom;
	protocol[SUB(TX_ERR) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)ethernet_busy + rom;
	protocol[SUB(BUSY) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)return_to_pool + rom;
	protocol[SUB(RTP) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)ethernet_status + rom;
	protocol[SUB(STATUS) | ETHERNET_STATE] = (int (*)())adr;
	adr = (unsigned)ethernet_abort + rom;
	protocol[SUB(ABORT) | ETHERNET_STATE] = (int (*)())adr;

	gct->driver->child[n].upper = scc->upper;
	gct->driver->child[n].state = ETHERNET_STATE;
	gct->driver->child[n].smac = &protocol[ETHERNET_STATE];
	/*
	 * set constant ETHERNET status bits for
	 * first BD's in a frame and
	 * last BD's in a frame
	 */
	wa->BDlast = T_I | T_L | T_C | T_PAD;
	wa->BDfirst = 0;
	wa->BDrx = 0;
	wa->intr_rxfr = ETHERNET_RXF;
	wa->intr_txbd = ETHERNET_TXB;
	wa->intr_terr = ETHERNET_TXE;
	wa->intr_busy = ETHERNET_BSY;
	wa->fr_lng = scc->mrblr;
	wa->t_error = ETHERNET_T_ERROR;
	wa->max_ind_addr = scc->max_ind_addr;
	wa->max_grp_addr = scc->max_grp_addr;

	/*
	 * initialize ethernet parameter ram
	 */
	wa->enet_pram->c_mask = scc->pram.e.c_mask;
	wa->enet_pram->c_pres = scc->pram.e.c_pres;
	wa->enet_pram->crcec = scc->pram.e.crcec;
	wa->enet_pram->alec = scc->pram.e.alec;
	wa->enet_pram->disfc = scc->pram.e.disfc;
	wa->enet_pram->pads = scc->pram.e.pads;
	wa->enet_pram->ret_lim = scc->pram.e.ret_lim;
	wa->enet_pram->mflr = scc->pram.e.mflr;
	wa->enet_pram->minflr = scc->pram.e.minflr;
	wa->enet_pram->maxd1 = scc->pram.e.maxd1;
	wa->enet_pram->maxd2 = scc->pram.e.maxd2;
	wa->enet_pram->gaddr1 = scc->pram.e.gaddr1;
	wa->enet_pram->gaddr2 = scc->pram.e.gaddr2;
	wa->enet_pram->gaddr3 = scc->pram.e.gaddr3;
	wa->enet_pram->gaddr4 = scc->pram.e.gaddr4;
	wa->enet_pram->paddr_h = scc->pram.e.paddr_h;
	wa->enet_pram->paddr_m = scc->pram.e.paddr_m;
	wa->enet_pram->paddr_l = scc->pram.e.paddr_l;
	wa->enet_pram->p_per = scc->pram.e.p_per;
	wa->enet_pram->iaddr1 = scc->pram.e.iaddr1;
	wa->enet_pram->iaddr2 = scc->pram.e.iaddr2;
	wa->enet_pram->iaddr3 = scc->pram.e.iaddr3;
	wa->enet_pram->iaddr4 = scc->pram.e.iaddr4;
	wa->enet_pram->taddr_h = scc->pram.e.taddr_h;
	wa->enet_pram->taddr_m = scc->pram.e.taddr_m;
	wa->enet_pram->taddr_l = scc->pram.e.taddr_l;

}

/********************************************************
 * routine:	ethernet_add_addr
 *
 * description:
 *	Add an ETHERNET address to the QUICC's individual
 *	or group hash table.
 *	The routine first checks that the related child
 *	is connected to an SCC that runs the ETHERNET protocol.
 *	The address is added to the hash table by executing
 *	an appropriate  QUICC command.
 *	The address is also inserted to the child work area
 *	group or individual table approprietly.
 *
 * arguments:
 *	m	points to the message received.
 *		m->hdr.id indicates the child id
 *		The first 6 bytes of m->param contain
 *		the ethernet address that should be inserted.
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
ethernet_add_addr(m)
MSG *m;
{
	GCT *gct;
	CHILD *child;
	WORK_AREA *wa;
	ENET_ADDR *addr, *p;
	unsigned int	i;

	GETGCT(gct);
	child = &gct->driver->child[m->hdr.id];
	wa = (WORK_AREA *)child->work_area;

	/*
	 * Check if the scc runs the ethernet protocol;
	 */
	if( child->state != ETHERNET_STATE )
		return(NOT_ALLOWED);
	addr = (ENET_ADDR *)&(m->param[0]);

	if( addr->high_addr & GROUP_ADDR ) {
		if ( (find_grp_addr(addr, wa)) || !wa->max_grp_addr )
			return(OP_FAILED);
	}
	else if( (find_ind_addr(addr, wa)) || !wa->max_ind_addr )
			return(OP_FAILED);

	/*
	 * if there is room in the address table, insert
	 * it, else return and do not insert it to the
	 * QUICC's hash table.
	 */
	if( addr->high_addr & GROUP_ADDR ) {
		p = wa->grp_addrs;
		for(i = 0; i < wa->max_grp_addr; i++) {
			/*
			 * The group bit set to 0 indicates the
			 * end of the table.
			 */
			if( !(p->high_addr & 0x80000000) ) {
				p->high_addr = addr->high_addr;
				p->low_addr = addr->low_addr;
				break;
			}
			p++;
		}
		if( i >= wa->max_grp_addr )
			return(OP_FAILED);
	}
	else {
		p = wa->ind_addrs;
		for(i = 0; i < wa->max_ind_addr; i++) {
			/*
			 * The group bit set to 1 indicates the
			 * end of the table.
			 */
			if( p->high_addr & 0x80000000 ) {
				p->high_addr = addr->high_addr;
				p->low_addr = addr->low_addr;
				break;
			}
			p++;
		}
		if( i >= wa->max_ind_addr )
			return(OP_FAILED);
	}

	/*
	 * insert the address to the QUICCs hash table
	 * The hash table should be written in Intel format.
	 */
	wa->enet_pram->taddr_h = (((addr->low_addr & 0xff) << 8) |
		((addr->low_addr & 0xff00) >> 8));
	wa->enet_pram->taddr_m = (((addr->high_addr & 0xff) << 8) |
		((addr->high_addr & 0xff00) >> 8));
	wa->enet_pram->taddr_l = (((addr->high_addr & 0x00ff0000) >> 8)
		| ((addr->high_addr & 0xff000000) >> 24));
	issue_cmd(SET_ENET_GROUP + (wa->channel << 6), wa->quicc);
	return(SUCCESS);
}

/********************************************************
 * routine:	ethernet_clr_grp_addrs
 *
 * description:
 *	Clear the group hash table and the child wa group
 *	table. 
 *	This is done by setting the group address of all
 *	addresses in the table to 0.
 *
 * arguments:
 *	m	points to the message received.
 *		m->hdr.id indicates the child id
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
ethernet_clr_grp_addrs(m)
MSG *m;
{
	GCT *gct;
	CHILD *child;
	WORK_AREA *wa;
	ENET_ADDR *addr, *p;
	unsigned int	i;

	GETGCT(gct);
	child = &gct->driver->child[m->hdr.id];
	wa = (WORK_AREA *)child->work_area;
	/*
	 * Check if the scc runs the ethernet protocol;
	 */
	if( (child->state != ETHERNET_STATE) || !wa->max_grp_addr )
		return(NOT_ALLOWED);

	/*
	 * Clear group addresses table in work area
	 */
	p = wa->grp_addrs;
	for(i = 0; i < wa->max_grp_addr; i++) {
		p->high_addr = 0;
		p->low_addr = 0;
		p++;
	}

	/*
	 * Clear QUICC's hash table
	 */
	wa->enet_pram->gaddr1 = 0;
	wa->enet_pram->gaddr2 = 0;
	wa->enet_pram->gaddr3 = 0;
	wa->enet_pram->gaddr4 = 0;
	return(SUCCESS);
}

/********************************************************
 * routine:	ethernet_clr_ind_addrs
 *
 * description:
 *	Clear the individual hash table and the child wa
 *	individual table. 
 *	This is done by setting the group address of all
 *	addresses in the table to 1.
 *
 * arguments:
 *	m	points to the message received.
 *		m->hdr.id indicates the child id
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
ethernet_clr_ind_addrs(m)
MSG *m;
{
	GCT *gct;
	CHILD *child;
	WORK_AREA *wa;
	ENET_ADDR *addr, *p;
	unsigned int	i;

	GETGCT(gct);
	child = &gct->driver->child[m->hdr.id];
	wa = (WORK_AREA *)child->work_area;
	/*
	 * Check if the scc runs the ethernet protocol;
	 */
	if( (child->state != ETHERNET_STATE) || !wa->max_ind_addr )
		return(NOT_ALLOWED);

	/*
	 * Clear individual addresses table in work area
	 */
	p = wa->ind_addrs;
	for(i = 0; i < wa->max_ind_addr; i++) {
		p->high_addr = 0x80000000;
		p->low_addr = 0x80000000;
		p++;
	}

	/*
	 * Clear QUICC's hash table
	 */
	wa->enet_pram->iaddr1 = 0;
	wa->enet_pram->iaddr2 = 0;
	wa->enet_pram->iaddr3 = 0;
	wa->enet_pram->iaddr4 = 0;
	return(SUCCESS);
}

/********************************************************
 * routine:	find_grp_addr
 *
 * description:
 *	Check if an ethernet address resides in the childs
 *	group address table.
 *
 * arguments:
 *	addr	points to an ETHERNET address structure.
 *	wa	points to the childs work area.
 *
 * return code:
 *	0	- the address is not in the table.
 *	else	- the address is found.
 *
 * side effects:
 *
 ********************************************************/
find_grp_addr(addr, wa)
ENET_ADDR *addr;
WORK_AREA *wa;
{
	unsigned int	i;
	ENET_ADDR *p;

	p = wa->grp_addrs;
	for(i = 0; i < wa->max_grp_addr; i++) {
		/*
		 * The group bit set to 0 indicates the
		 * end of the table.
		 */
		if( !(p->high_addr & 0x80000000) )
			return(0);
		if( (p->high_addr == addr->high_addr) &&
			(p->low_addr == addr->low_addr) )
			return(1);
		p++;
	}
	return(0);
}

/********************************************************
 * routine:	find_ind_addr
 *
 * description:
 *	Check if an ethernet address resides in the childs
 *	individual address table.
 *
 * arguments:
 *	addr	points to an ETHERNET address structure.
 *	wa	points to the childs work area.
 *
 * return code:
 *	0	- the address is not in the table.
 *	else	- the address is found.
 *
 * side effects:
 *
 ********************************************************/
find_ind_addr(addr, wa)
ENET_ADDR *addr;
WORK_AREA *wa;
{
	unsigned int	i;
	ENET_ADDR *p;

	p = wa->ind_addrs;
	for(i = 0; i < wa->max_ind_addr; i++) {
		/*
		 * The group bit set to 1 indicates the
		 * end of the table.
		 */
		if( (p->high_addr & 0x80000000) )
			return(0);
		if( (p->high_addr == addr->high_addr) &&
			(p->low_addr == addr->low_addr) )
			return(1);
		p++;
	}
	return(0);
}


/********************************************************
 * routine:	ethernet_tables_init
 *
 * description:
 *	Allocate space for the ethernet address tables.
 *	For each child that runs ETHERNET allocate a
 *	2 tables in the drivers bss. The tables will be
 *	allocated after the timer tables.
 *	After allocating the tables initiate them with
 *	0 for a group table and 0x80000000 for an
 *	individual table.
 *
 * arguments:
 *
 * return code:
 *
 * side effects:
 *
 ********************************************************/
ethernet_tables_init()
{
	GCT *gct;
	GWA *gwa;
	int i, j;
	CHILD *child;
	WORK_AREA *wa;
	ENET_ADDR *p;

	GETGCT(gct);
	gwa = (GWA *)gct->driver->work_area;

	/*
	 * allocate memory in driver_bss for timer table
	 */
	p = (ENET_ADDR *)&(gwa->timer[gwa->num_timers]);
	for(i = 0; i < gct->driver->max; i++){
		child = &gct->driver->child[i];
		if( child->state != ETHERNET_STATE )
			continue;
		wa = (WORK_AREA *)child->work_area;
		wa->ind_addrs = p;
		for( j = 0; j < wa->max_ind_addr; j++ )
			p[j].high_addr = 0x80000000;
		p += wa->max_ind_addr;
		wa->grp_addrs = p;
		for( j = 0; j < wa->max_grp_addr; j++ )
			p[j].high_addr = 0;
		p += wa->max_grp_addr;
	}
}

ethernet_status()
{
}
