/* Copyright 1984 by the Massachusetts Institute of Technology */


/* 10 MBit EtherNet driver - contains interface modules for the
 * gateway process which actually talk to this particular
 * network.
 */


/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/gw/nets/RCS/ether.c,v $
 * $Revision: 1.2 $
 * $Date: 87/10/12 01:57:33 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	ether.c,v $
 * Revision 1.2  87/10/12  01:57:33  jon
 * Adds NIP dispatcher.
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_ether_c = "$Header: ether.c,v 1.2 87/10/12 01:57:33 jon Locked $";
#endif	lint

#include	<types.h>
#include	<sys.h>
#include	"../src/const.h"
#include	"../src/param.h"
#include	"../src/defs.h"
#include	"../src/macs.h"
#include	"../src/net.h"
#include	"../src/ext.h"
#include	"../in/in.h"
#include	"../ch/ch.h"
#include	"../arp/arp.h"

#include	"ether.h"


/* Error messages */

static char ethinerr[] = "Input error frm net %o intf %s/%o\n";
static char ethbdtp[] = "Bad type %x frm net %o intf %s/%o\n";
static char ethtolg[] = "Pkt too long for net %o intf %s/%o\n";
static char ethsarp[] = "Need to send add res pkt for addr %o,%o,%o,%o\n";

char	ETHBRD[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

/* Protocol initialization routine.  Initializes the necessary fields
 * in the ARP descriptor.
 */

e10_prinit(netp, prot, nm)
net	*netp;
unsw	prot;
word	nm;
{
    char *e10_paddr();

    if (netp->n_type != ((unsb) T_E10))
      iflog(L_PANIC)
	dolog("e10_prinit called, but not EtherNet");
    
    switch (prot) {
    case P_IN:
    case P_CH:
	return;	
    case P_ARP:
	((arp_desc *)nm)->arpd_phaddr = e10_paddr;
	((arp_desc *)nm)->arpd_hrd = ARP_HRD_ETHERNET;
	((arp_desc *)nm)->arpd_hln = 6;
	switch(((arp_desc *)nm)->arpd_protocol) {
	case P_IN:
	    ((arp_desc *)nm)->arpd_pro = ET_IN;
	    ((arp_desc *)nm)->arpd_pln = sizeof(inaddr);
	    break;
	case P_CH:
	    ((arp_desc *)nm)->arpd_pro = ET_CH;
	    ((arp_desc *)nm)->arpd_pln = sizeof(chaddr);
	    break;
	default:
	    bughalt("Bad protocol for EtherNet ARP");
	}
	return;
    default:
	bughalt("Bad protocol for EtherNet");
    }
}


/* This routine is called when a packet is received, and must
 * correctly dispose of it.
 */

e10_in(iob)
reg	iorb	*iob;

{	reg	e10pkt	*pkt;
	reg	net	*netp;
	void		arp_in();
	
#ifdef	BS_BUS
	bswap(iob->i_addr, iob->i_bxfr);
#endif	BS_BUS

	netp = mknet(iob->i_usr2);
	pkt = mke10pkt(iob->i_addr);
	if (pkt->e10_dst[0] & 0x01)	/* if multicast or broadcast */
		iob->i_stat |= I_BRD;

	iob->i_addr += sizeof(e10pkt);
	iob->i_bxfr -= sizeof(e10pkt);
	switch (pkt->e10_type) {

	case ET_IN:	inaddq(iob);
			break;

	case ET_CH:	chaddq(iob);
			break;

	case ET_AR:	/* addtsk(syshnd, ARPPRI, arp_in, iob); */
			arp_in(iob);
			break;

	case ET_NIP:	nip_in(iob);
	    		break;

	/* Just echo the packet */
	case ET_MD:	copy(pkt->e10_src, pkt->e10_dst, 6);
			iob->i_addr -= sizeof(e10pkt);
			iob->i_breq = iob->i_bxfr + sizeof(e10pkt);
			iob->i_usr3 = ANYHOST;
			netsend(iob, netp);
			break;

	default:	iflog(L_INFC)
			    dolog(ethbdtp, swab(pkt->e10_type), mksnet(netp));
			netp->n_disc++;
			freebuf(iob);
	}
}


/* Routine that adds the local net header for output.
 */

word	e10_out(iob, netp, prot, nm)
reg	iorb	*iob;
net	*netp;
unsw	prot;
word	nm;
{
	reg	e10pkt	*pkt;

	iob->i_addr -= sizeof(e10pkt);
	iob->i_breq += sizeof(e10pkt);
	pkt = mke10pkt(iob->i_addr);
			
	switch (prot) {
	case P_IN:
	    if (nm == ANYHOST)
	      copy(ETHBRD, pkt->e10_dst, 6);
	    else {
		if (arp_set_addr(iob, netp, P_IN, nm, pkt->e10_dst)) {
		    iob->i_addr += sizeof(e10pkt);
		    iob->i_breq -= sizeof(e10pkt);
		    return(D_OK);
		}
	    }
	    pkt->e10_type = ET_IN;
	    break;

	case P_CH:
	    if (nm == ANYHOST)
	      copy(ETHBRD, pkt->e10_dst, 6);
	    else {
		if (arp_set_addr(iob, netp, P_CH, nm, pkt->e10_dst)) {
		    iob->i_addr += sizeof(e10pkt);
		    iob->i_breq -= sizeof(e10pkt);
		    return(D_OK);
		}
	    }
	    pkt->e10_type = ET_CH;
	    break;
	    
	case P_ARP:
	    if (nm == ANYHOST)
	      copy(ETHBRD, pkt->e10_dst, 6);
	    else
	      copy(nm, pkt->e10_dst, 6);
	    pkt->e10_type = ET_AR;
	    break;

	case ET_NIP:
	    copy(((int)&(pkt->e10_type))+2, pkt->e10_dst, 6);
	    pkt->e10_type = ET_NIP;
	    break;

	default:
	    iflog(L_ERRU)
	      dolog(ethbdtp, swab(prot), mksnet(netp));
	    return(D_ERR);
	}

	copy(netp->n_odevp->d_dev1, pkt->e10_src, 6);
	iob->i_usr3 = ANYHOST;
	if (iob->i_breq > EMAX) {
		iflog(L_FATAL)
			dolog(ethtolg, mksnet(netp));
		freebuf(iob);
		return(D_ERR);
	}

#ifdef	BS_BUS
	bswap(iob->i_addr, iob->i_breq);
#endif	BS_BUS

	return(netsend(iob, netp));
}

/* Fills a character buffer with an ethernet address for printing. */
char *e10_paddr(buflen, buf, nm)
int buflen;
char *buf;
byte *nm;
{
    char *snprintf();

    return(snprintf(buflen, buf, "%02x%02x%02x%02x%02x%02x",
		    nm[0], nm[1], nm[2], nm[3], nm[4], nm[5]));
}
