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


/* VII network driver - contains interface modules for the
 * gateway process which actually talk to this particular
 * network. Since this network is an MPW one, we use MPW
 * protocol conventions.
 */

#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	"mpw.h"
#include	"vii.h"



/* Error messages */

static char viibdmv[] = "Bad MV pkt hdr %d %d frm net %d intfc %s/%d, dsc\n";
static char viibdmw[] = "Bad MW pkt hdr %d %d frm net %d intfc %s/%d, dsc\n";
static char viioch[] = "Old fmt Chaos ring pkt frm hst %d net %d intfc %s/%d, dsc\n";
static char viibcc[] = "Bd chaos cksum %04x frm net %d intfc %s/%d, dsc\n";


/* Protocol initialization routine. At the moment, can't really do much.
 */

vii_prinit(netp, prot, nm)
net	*netp;
unsw	prot;
word	nm;
{
    if (netp->n_type != ((unsb) T_VII))
      iflog(L_PANIC)
	dolog("vii_prinit called, but not VII");
    
    switch (prot) {
    case P_IN:	
    case P_CH:
	return;
		
    default:	bughalt("Bad protocol for VII LNI");
    }
}


/* This routine is called when a packet is received, and must
 * correctly dispose of it. The reason that you didn't leave room
 * for the MPW header is that the packet may not have a MPW header
 * on it, which may result in the data being lower in the buffer
 * than you might want.
 */

vii_in(iob)
reg	iorb	*iob;
{
    reg	mpwpkt	*pkt;
    reg	net	*netp;

#ifdef	BS_BUS
    bswap(iob->i_addr, iob->i_bxfr);
#endif	BS_BUS
	
    if (((struct viipkt *)iob->i_addr)->vii_dst == VIIBRD)
      iob->i_stat |= I_BRD;
    iob->i_addr += sizeof(struct viipkt);
    iob->i_bxfr -= sizeof(struct viipkt);
    pkt = ((mpwpkt *) iob->i_addr);

    switch (pkt->m_ver) {
    case MV_MW:
	iob->i_addr += sizeof(mpwpkt);
	iob->i_bxfr -= sizeof(mpwpkt);

	switch (pkt->m_prot) {
	case MW_IN:
	    inaddq(iob);
	    return;
	    
	case MW_CH:
	    if (iob->i_bxfr & 1)
	      iob->i_addr[iob->i_bxfr] = 0;
	    if ((cksum(pkt, (iob->i_bxfr+sizeof(mpwpkt)+1)/2, 0) + 1) != 0) {
		netp = mknet(iob->i_usr2);
		iflog(L_ERRU)
		  dolog(viibcc, pkt->m_rsd, mksnet(netp));
		netp->n_disc++;
		freebuf(iob);
		return;
	    }
	    chaddq(iob);
	    return;
	    
	default:
	    netp = mknet(iob->i_usr2);
	    iflog(L_ERRU)
	      dolog(viibdmw, pkt->m_ver,
		    pkt->m_prot, mksnet(netp));
	    netp->n_disc++;
	    freebuf(iob);
	    return;
	}
	
    case MV_CH:
	netp = mknet(iob->i_usr2);
	iflog(L_INFC)
	  dolog(viioch,
		((struct viipkt *)(iob->i_addr - sizeof(struct viipkt)))->vii_src,
		mksnet(netp));
	netp->n_disc++;
	freebuf(iob);
/*	chaddq(iob); */
	return;
	
    default:
	netp = mknet(iob->i_usr2);
	iflog(L_INFU)
	  dolog(viibdmv, pkt->m_ver, pkt->m_prot,
		mksnet(netp));
	netp->n_disc++;
	freebuf(iob);
    }
}


/* Routine that prepares a packet for output.
 */

word	vii_out(iob, netp, prot, nm)
reg	iorb	*iob;
net	*netp;
unsw	prot;
word	nm;
{
    reg	struct	viipkt	*hdr;
    reg	mpwpkt	*mhdr;
    
    switch (prot) {
	
    case P_IN:
	mhdr = mkmpw(iob->i_addr - sizeof(mpwpkt));
	mhdr->m_ver = MV_MW;
	mhdr->m_prot = MW_IN;
	mhdr->m_rsd = 0;
	hdr = ((struct viipkt *)
	       (((char *) mhdr) - sizeof(struct viipkt)));
	if (nm != ANYHOST)
	  hdr->vii_dst = mkina(nm)->i_byte.i_bytel;
	iob->i_addr = ((byte *) hdr);
	iob->i_breq += (sizeof(mpwpkt) +
			sizeof(struct viipkt));
	break;
		
	/* this is for old style chaos pkts */
/*    case P_CH:
	hdr = ((struct viipkt *)
	       (iob->i_addr - sizeof(struct viipkt)));
	if (nm != ANYHOST)
	  hdr->vii_dst = mkcha(nm)->oc_addr.oc_hst;
	iob->i_addr = ((byte *) hdr);
	iob->i_breq += sizeof(struct viipkt);
	break;
 */

    case P_CH:
	mhdr = mkmpw(iob->i_addr - sizeof(mpwpkt));
	mhdr->m_ver = MV_MW;
	mhdr->m_prot = MW_CH;
	mhdr->m_rsd = 0;
	if (iob->i_breq & 1)
	  iob->i_addr[iob->i_breq] = 0;
	mhdr->m_rsd = ~cksum(mhdr, (iob->i_breq + sizeof(mpwpkt) + 1)/2, 0);
	hdr = ((struct viipkt *)
	       ((char *)mhdr - sizeof(struct viipkt)));
	if (nm != ANYHOST)
	  hdr->vii_dst = mkcha(nm)->oc_addr.oc_hst;
	iob->i_addr = ((byte *) hdr);
	iob->i_breq += sizeof(mpwpkt) + sizeof(struct viipkt);
	break;
		
    default:
	bughalt("Bad prot in VII O/P");
    }
    
    if (nm == ANYHOST) {
	hdr->vii_dst = VIIBRD;
	iob->i_usr3 = ANYHOST;
    }
    else {
	if (hdr->vii_dst == VIIBRD) return(D_BRD);
	iob->i_usr3 = hdr->vii_dst;
    }
    
    iob->i_breq = roundup(iob->i_breq);
    if (iob->i_breq > VIIMAX)
      bughalt("Pkt too long for VII LNI");
    
#ifdef	BS_BUS
    bswap(iob->i_addr, iob->i_breq);
#endif	BS_BUS
    
    return(netsend(iob, netp));
}
