2.9BSD/usr/net/sys/net/if_un.c

Compare this file to the similar file:
Show the results in this format:

/*	if_un.c	4.19	82/06/23	*/

/*
 * Ungermann-Bass network/DR11-W interface driver
 */

#include "un.h"
#if NUN > 0

#include "param.h"
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/ubavar.h>
#include <sys/unreg.h>
#include "../net/in.h"
#include "../net/in_systm.h"
#include "../net/if.h"
#include "../net/if_un.h"
#include "../net/if_uba.h"
#include "../net/ip.h"
#include "../net/ip_var.h"
#include "../net/route.h"
#include <errno.h>

#define	UNMTU		(600-sizeof (struct un_header))

#define	US_NULL		0	/* not doing anything state */
#define	US_IDLE 	1	/* waiting to transfer state */
#define	US_READ		2	/* reading state */
#define	US_WRITE	3	/* writing state */
#define	US_RESET	4	/* waiting for reset state */

int	unprobe(), unattach(), unintr();
struct	uba_device *uninfo[NUN];
u_short	unstd[] = { 0 };
struct	uba_driver undriver =
	{ unprobe, 0, unattach, 0, unstd, "un", uninfo };
#define	UNUNIT(dev)	(minor(dev))

int	uninit(), unoutput(), unreset();
int	unrestart();

/*
 * Ungermann-Bass software status per interface.
 *
 * Each interface is referenced by a network interface structure,
 * us_if, which the routing code uses to locate the interface.
 * This structure contains the output queue for the interface, its address,
 * etc.  We also have, for each interface, a UBA interface structure, which
 * contains information about the UNIBUS resources held by the interface:
 * map registers, buffered data paths, etc.  Information is cached in this
 * structure for use by the if_uba.c routines in running the interface
 * efficiently.
 */
struct un_softc {
	struct	ifnet us_if;		/* network-visible interface */
	struct	ifuba us_ifuba;		/* UNIBUS resources */
	short	us_state;		/* device state */
	short	us_errcnt;		/* number of errors since time set */
	short	us_restart;		/* restart interval */
	u_char	us_maxtime;		/* interval for error counting */
	u_char	us_maxerr;		/* errors allowed in interval */
	time_t	us_errtime;		/* time for error counting */
} un_softc[NUN];

/*
 * Cause an interrupt to determine interface presence and
 * interrupt vector.
 */
unprobe(reg)
	caddr_t reg;
{
	register int br, cvec;		/* r11, r10 value-result */
	register struct undevice *addr = (struct undevice *)reg;

#ifdef lint
	br = 0; cvec = br; br = cvec;
	unintr(0);
#endif
	addr->csr = IE|UNRESET;
	addr->csr = IE|UNRESET|GO;
	DELAY(100000);
	addr->csr = 0;
#ifdef ECHACK
	br = 0x16;
#endif
	return (1);
}

/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.
 */
unattach(ui)
	struct uba_device *ui;
{
	register struct un_softc *us = &un_softc[ui->ui_unit];
	struct sockaddr_in *sin;

	us->us_if.if_unit = ui->ui_unit;
	us->us_if.if_name = "un";
	us->us_if.if_mtu = UNMTU;
	us->us_if.if_net = ui->ui_flags;
	sin = (struct sockaddr_in *)&us->us_if.if_addr;
	sin->sin_family = AF_INET;
	/* host number will be filled in later. */
	sin->sin_addr = if_makeaddr(us->us_if.if_net, 0);
	sin = (struct sockaddr_in *)&us->us_if.if_broadaddr;
	sin->sin_family = AF_INET;
	sin->sin_addr = if_makeaddr(us->us_if.if_net, INADDR_ANY);
	us->us_if.if_flags |= IFF_BROADCAST;
	us->us_if.if_init = uninit;
	us->us_if.if_output = unoutput;
	us->us_if.if_ubareset = unreset;
	us->us_if.if_watchdog = unrestart;
	us->us_maxtime = 3;
	us->us_maxerr = 10;
	us->us_restart = 5 * 60 * hz;
	us->us_ifuba.ifu_flags = UBA_CANTWAIT;
#ifdef notdef
	us->us_ifuba.ifu_flags |= UBA_NEEDBDP;
#endif
	if_attach(&us->us_if);
}

/*
 * Reset of interface after UNIBUS reset.
 * If interface is on specified uba, reset its state.
 */
unreset(unit, uban)
	int unit, uban;
{
	register struct uba_device *ui;

	if (unit >= NUN || (ui = uninfo[unit]) == 0 || ui->ui_alive == 0 ||
	    ui->ui_ubanum != uban)
		return;
	printf(" un%d", unit);
	uninit(unit);
}

/*
 * Initialization of interface; clear recorded pending
 * operations, and reinitialize UNIBUS usage.
 */
uninit(unit)
	int unit;
{
	register struct un_softc *us = &un_softc[unit];
	register struct uba_device *ui = uninfo[unit];
	register struct undevice *addr;
	int s;

	if (if_ubainit(&us->us_ifuba, ui->ui_ubanum,
	    sizeof (struct un_header), (int)btoc(UNMTU)) == 0) {
		printf("un%d: can't initialize\n", unit);
		us->us_if.if_flags &= ~IFF_UP;
		return;
	}
	us->us_errcnt = 0;
	us->us_errtime = time;
	unwhoami(unit);

	/*
	 * Reset U-B interface, thus causing an interrupt which
	 * will start things going.
	 */
	addr = (struct undevice *)ui->ui_addr;
	s = splimp();
	addr->csr = IE|UNRESET;
	addr->csr = IE|UNRESET|GO;
	us->us_state = US_RESET;
	splx(s);
}

/*
 * Try to start a write operation.
 * If interface is busy, it must be in idle state, so issue a reset.
 * Otherwise, get the datagram from the output queue, map it onto
 * the UNIBUS, and start the write.  This routine should not be
 * called if the output queue is empty.
 */
unstart(dev)
	dev_t dev;
{
	int unit = UNUNIT(dev);
	struct uba_device *ui = uninfo[unit];
	register struct un_softc *us = &un_softc[unit];
	register struct undevice *addr = (struct undevice *)ui->ui_addr;
	struct mbuf *m;
	int dataaddr, datalen;
	register short cmdcsr;

	if (us->us_state != US_NULL) {
		addr->csr = IE|UNRESET;
		addr->csr = IE|UNRESET|GO;
		us->us_state = US_RESET;
	} else {
		IF_DEQUEUE(&us->us_if.if_snd, m);
		if (m == 0)
			return;
		us->us_state = US_WRITE;
		datalen = if_wubaput(&us->us_ifuba, m);
		if (us->us_ifuba.ifu_flags & UBA_NEEDBDP)
			UBAPURGE(us->us_ifuba.ifu_uba,
				us->us_ifuba.ifu_w.ifrw_bdp);
		dataaddr = us->us_ifuba.ifu_w.ifrw_info;
		addr->bar = dataaddr & 0xffff;
		addr->wcr = -(((datalen + 1) >> 1) + 1);
		cmdcsr = ((dataaddr >> 12) & 0x30) | IE | UNOUT;
		addr->csr = cmdcsr;
		addr->csr = cmdcsr | GO;
	}
}

/*
 * Ungermann-Bass interface interrupt handler.
 * Determines reason for interrupt and acts accordingly.
 */
unintr(unit)
	int unit;
{
	register struct un_softc *us = &un_softc[unit];
	struct undevice *addr = (struct undevice *)uninfo[unit]->ui_addr;
	register struct un_header *un;
	struct mbuf *m;
	int len;
	register struct ifqueue *inq;
	int cmdcsr;

	if ((addr->dar & RESETACK) && us->us_state != US_RESET) {
		if ((us->us_if.if_flags & IFF_UP) == 0)
			return;
		printf("un%d: unexpected reset\n", unit);
		unerror(unit);
	}
		
	switch (us->us_state) {

	case US_NULL:
		printf("un%d: stray interrupt\n", unit);
		break;

	case US_RESET:
		if (!(addr->dar & RESETACK)) {
			addr->csr = IE|UNRESET;
			addr->csr = IE|UNRESET|GO;
			return;
		}
		break;

	case US_IDLE:
		break;

	case US_READ:
		us->us_if.if_ipackets++;
		if (us->us_ifuba.ifu_flags & UBA_NEEDBDP)
			UBAPURGE(us->us_ifuba.ifu_uba,
				us->us_ifuba.ifu_r.ifrw_bdp);
		if (addr->csr & STATA) {
			if ((us->us_if.if_flags & IFF_UP) == 0)
				return;
			printf("un%d: input error csr=%b\n", unit,
				addr->csr&0xffff, UNBITS);
			us->us_if.if_ierrors++;
			unerror(unit);
			break;
		}
		un = (struct un_header *)(us->us_ifuba.ifu_r.ifrw_addr);
		switch (un->un_ptype) {
#ifdef INET
		case UNTYPE_IP:
			len = htons((u_short)((struct ip *) (un+1))->ip_len);
			schednetisr(NETISR_IP);
			inq = &ipintrq;
			break;
#endif
		case UNTYPE_INQUIRE: {
			struct sockaddr_in *sin;

			us->us_if.if_host[0] =
			    un->un_dport << 16 | htons(un->un_dniu);
			sin = (struct sockaddr_in *)&us->us_if.if_addr;
			sin->sin_addr = if_makeaddr(us->us_if.if_net,
				us->us_if.if_host[0]);
			us->us_if.if_flags |= IFF_UP;
			if_rtinit(&us->us_if, RTF_UP);
			goto setup;
		}

		default:
			printf("un%d: bad packet type %d\n", un->un_ptype);
			goto setup;
		}

		m = if_rubaget(&us->us_ifuba, len, 0);
		if (m != 0)
			if (IF_QFULL(inq)) {
				IF_DROP(inq);
				m_freem(m);
			} else
				IF_ENQUEUE(inq, m);
		break;

	case US_WRITE:
		us->us_if.if_opackets++;
		if (addr->csr & STATA) {
			if ((us->us_if.if_flags & IFF_UP) == 0)
				return;
			printf("un%d: output error csr=%b\n",
			    unit, addr->csr, UNBITS);
			us->us_if.if_oerrors++;
			unerror(unit);
		}
		if (us->us_ifuba.ifu_xtofree) {
			m_freem(us->us_ifuba.ifu_xtofree);
			us->us_ifuba.ifu_xtofree = 0;
		}
		break;

	default:
		printf("un%d: invalid state %d csr=%b\n",
		    us->us_state, addr->csr, UNBITS);
	}

setup:
	us->us_state = US_NULL;
	if (addr->csr & STATB) {
		us->us_state = US_READ;
		addr->wcr = -((sizeof (struct un_header) + UNMTU + 1)/2+1);
		addr->bar = us->us_ifuba.ifu_r.ifrw_info & 0xffff;
		cmdcsr = ((us->us_ifuba.ifu_r.ifrw_info >> 12) & 0x30);
		cmdcsr |= IE|UNRDDG;
		addr->csr = cmdcsr;
		addr->csr = cmdcsr | GO;
	} else if (us->us_if.if_snd.ifq_head != 0 && (addr->csr & STATC))
		unstart(unit);
	
	if (us->us_state == US_NULL) {
		us->us_state = US_IDLE;
		addr->csr = IE|UNIDLE;
		addr->csr = IE|UNIDLE|GO;
	}
}

/*
 * Ungermann-Bass output routine.
 * Encapsulate a packet destined for dst for the local net.
 */
unoutput(ifp, m0, dst)
	struct ifnet *ifp;
	struct mbuf *m0;
	struct sockaddr *dst;
{
	int type, destniu, destport, len;
	register struct mbuf *m = m0;
	register struct un_header *un;
	register struct un_softc *us = &un_softc[ifp->if_unit];
	int s;

	if ((us->us_if.if_flags & IFF_UP) == 0)
		return (ENETDOWN);
	switch (dst->sa_family) {

#ifdef INET
	case AF_INET: {
		struct sockaddr_in *sin = (struct sockaddr_in *)dst;
		struct ip *ip = mtod(m, struct ip *);

		if (sin->sin_addr.s_addr & 0xffffff00) {
			destniu = sin->sin_addr.s_addr >> 24;
			destport = (sin->sin_addr.s_addr >> 8) & 0xff;
		} else {
			destniu = 0xffff;
			destport = 0xff;
		}
		len = htons((u_short) ip->ip_len);
		type = UNTYPE_IP;
		break;
	}
#endif
	default:
		printf("un%d: can't handle af%d\n", ifp->if_unit,
			dst->sa_family);
		m_freem(m0);
		return (EAFNOSUPPORT);
	}
	
	/*
	 * Add local net header.  If no space in first mbuf,
	 * allocate another.
	 */
	if (m->m_off > MMAXOFF ||
	    MMINOFF + sizeof (struct un_header) > m->m_off) {
		m = m_get(M_DONTWAIT);
		if (m == 0) {
			m_freem(m0);
			return (ENOBUFS);
		}
		m->m_next = m0;
		m->m_off = MMINOFF;
		m->m_len = sizeof (struct un_header);
	} else {
		m->m_off -= sizeof (struct un_header);
		m->m_len += sizeof (struct un_header);
	}
	un = mtod(m, struct un_header *);
	bzero((caddr_t)un, sizeof (struct un_header));
	un->un_length = htons((u_short)(len + sizeof (struct un_header)));
	un->un_dniu = htons((u_short)destniu);
	un->un_dport = destport;
	un->un_dtype = 5;
	un->un_sniu = htons((u_short)(ifp->if_host[0] >> 24));
	un->un_sport = (ifp->if_host[0] >> 8) & 0xff;
	un->un_stype = 5;
	un->un_ptype = type;

	/*
	 * Queue message on interface, and start output if interface
	 * not yet active.
	 */
	s = splimp();
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
		m_freem(m);
		splx(s);
		return (ENOBUFS);
	}
	IF_ENQUEUE(&ifp->if_snd, m);
	if (us->us_state == US_IDLE)
		unstart(ifp->if_unit);
	splx(s);
	return (0);
}

/*
 * U-B error handler, if maxerr errors have occured
 * in maxtime seconds, disable the interface.
 */
unerror(unit)
	int unit;
{
	register struct un_softc *us = &un_softc[unit];
	struct undevice *addr = (struct undevice *)uninfo[unit]->ui_addr;

	if (time - us->us_errtime > us->us_maxtime) {
		us->us_errtime = time;
		us->us_errcnt = 1;
	} else if (++us->us_errcnt >= us->us_maxerr) {
		printf("un%d: error limit exceeded\n", unit);
		us->us_if.if_flags &= ~IFF_UP;
		addr->csr = 0;
		us->us_if.if_timer = us->us_restart;
	}
}

unrestart(unit)
	int unit;
{
	register struct un_softc *us = &un_softc[unit];
	struct undevice *addr = (struct undevice *)uninfo[unit]->ui_addr;
	int s;

	us->us_if.if_flags |= IFF_UP;
	printf("un%d: restarting\n", unit);
	unwhoami(unit);
	s = splimp();
	addr->csr = IE|UNRESET;
	addr->csr = IE|UNRESET|GO;
	us->us_state = US_RESET;
	splx(s);
}

/*
 * Send a "Who am I?" message to the interface. 
 * Interface should respond with an copy of the
 * packet with its real address filled in.  The
 * message is placed at the head of the output queue.
 * An interface reset should be done next to start
 * things rolling.
 */
unwhoami(unit)             
	int unit;
{
	register struct mbuf *m;
	register struct un_softc *us = &un_softc[unit];
	register struct un_header *un;
	int s;

	if ((m = m_get(M_DONTWAIT)) == 0) 
		return;
	m->m_off = MMINOFF;
	m->m_len = sizeof(struct un_header);
	un = mtod(m, struct un_header *);
	bzero((caddr_t)un, sizeof (struct un_header));
	un->un_length = htons(sizeof (struct un_header));
	un->un_dtype = un->un_stype = 5;
	un->un_ptype = UNTYPE_INQUIRE;
	s = splimp();
	IF_PREPEND(&us->us_if.if_snd, m);
	splx(s);
}