BSD 4_3_Net_1 release
[unix-history] / sys / vaxif / if_vv.c
/*
* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#)if_vv.c 7.4 (Berkeley) 10/22/88
*/
#include "vv.h"
#if NVV > 0
/*
* Proteon ProNET-10 and ProNET-80 token ring driver.
* The name of this device driver derives from the old MIT
* name of V2LNI for the proNET hardware, would would abbreviate
* to "v2", but this won't work right in config. Thus the name is "vv".
*
* This driver is compatible with the Unibus ProNET 10 megabit and
* 80 megabit token ring interfaces (models p1000 and p1080).
* A unit may be marked as 80 megabit using "flags 1" in the
* config file.
*
* This driver is also compatible with the Q-bus ProNET 10 megabit and
* 80 megabit token ring interfaces (models p1100 and p1180), but
* only on a MicroVAX-II or MicroVAX-III. No attempt is made to
* support the MicroVAX-I.
*
* TRAILERS: This driver has a new implementation of trailers that
* is at least a tolerable neighbor on the ring. The offset is not
* stored in the protocol type, but instead only in the vh_info
* field. Also, the vh_info field, and the two shorts before the
* trailing header, are in network byte order, not VAX byte order.
*
* Of course, nothing but BSD UNIX supports trailers on ProNET.
* If you need interoperability with anything else (like the p4200),
* turn off trailers using the -trailers option to /etc/ifconfig!
*
* HARDWARE COMPATABILITY: This driver prefers that the HSBU (p1001)
* have a serial number >= 040, which is about March, 1982. Older
* HSBUs do not carry across 64kbyte boundaries. They can be supported
* by adding "| UBA_NEED16" to the vs_ifuba.ifu_flags initialization
* in vvattach().
*
* The old warning about use without Wire Centers applies only to CTL
* (p1002) cards with serial <= 057, which have not received ECO 176-743,
* which was implemented in March, 1982. Most such CTLs have received
* this ECO.
*/
#include "../machine/pte.h"
#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "vmmac.h"
#include "errno.h"
#include "ioctl.h"
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#ifdef INET
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#endif
#include "../vax/cpu.h"
#include "../vax/mtpr.h"
#include "if_vv.h"
#include "if_uba.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
/*
* maximum transmission unit definition --
* you can set VVMTU at anything from 576 to 2036.
* 1536 is a popular "large" value, because it is a multiple
* of 512, which the trailer scheme likes.
* The absolute maximum size is 2036, which is enforced.
*/
#define VVMTU (2036)
#define VVMRU (VVMTU + (2 * sizeof(u_short)))
#define VVBUFSIZE (VVMRU + sizeof(struct vv_header))
#if VVMTU>2036
#undef VVMTU
#undef VVMRU
#undef VVBUFSIZE
#define VVBUFSIZE (2046)
#define VVMRU (VVBUFSIZE - sizeof (struct vv_header))
#define VVMTU (VVMRU - (2 * sizeof(u_short)))
#endif
/*
* debugging and tracing stuff
*/
int vv_tracehdr = 0; /* 1 => trace headers (slowly!!) */
#define vvtracehdr if (vv_tracehdr) vvprt_hdr
#define vvprintf if (vs->vs_if.if_flags & IFF_DEBUG) printf
/*
* externals, types, etc.
*/
int vvprobe(), vvattach(), vvreset(), vvinit();
int vvidentify(), vvstart(), vvxint(), vvwatchdog();
int vvrint(), vvoutput(), vvioctl();
struct uba_device *vvinfo[NVV];
u_short vvstd[] = { 0 };
struct uba_driver vvdriver =
{ vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
#define VVUNIT(x) minor(x)
#define LOOPBACK /* use loopback for packets meant for us */
#ifdef LOOPBACK
extern struct ifnet loif;
#endif
/*
* Software status of each interface.
*
* Each interface is referenced by a network interface structure,
* vs_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
* 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 vv_softc {
struct ifnet vs_if; /* network-visible interface */
struct ifuba vs_ifuba; /* UNIBUS resources */
u_short vs_host; /* this interface address */
short vs_oactive; /* is output active */
short vs_is80; /* is 80 megabit version */
short vs_olen; /* length of last output */
u_short vs_lastx; /* address of last packet sent */
u_short vs_lastr; /* address of last packet received */
short vs_tries; /* transmit current retry count */
short vs_init; /* number of ring inits */
short vs_refused; /* number of packets refused */
short vs_timeouts; /* number of transmit timeouts */
short vs_otimeout; /* number of output timeouts */
short vs_ibadf; /* number of input bad formats */
short vs_parity; /* number of parity errors on 10 meg, */
/* link data errors on 80 meg */
short vs_ipl; /* interrupt priority on Q-bus */
} vv_softc[NVV];
#define NOHOST 0xffff /* illegal host number */
/*
* probe the interface to see that the registers exist, and then
* cause an interrupt to find its vector
*/
vvprobe(reg, ui)
caddr_t reg;
struct uba_device *ui;
{
register int br, cvec;
register struct vvreg *addr;
#ifdef lint
br = 0; cvec = br; br = cvec;
#endif
addr = (struct vvreg *)reg;
/* reset interface, enable, and wait till dust settles */
#ifdef QBA
(void) spl6();
#endif
addr->vvicsr = VV_RST;
addr->vvocsr = VV_RST;
DELAY(100000);
/* generate interrupt by doing 1 word DMA from 0 in uba space!! */
addr->vvoba = 0; /* low 16 bits */
addr->vvoea = 0; /* extended bits */
addr->vvowc = -1; /* for 1 word */
addr->vvocsr = VV_IEN | VV_DEN; /* start the DMA, with interrupt */
DELAY(100000);
#ifdef QBA
vv_softc[ui->ui_unit].vs_ipl = br = qbgetpri();
#endif
addr->vvocsr = VV_RST; /* clear out the CSR */
if (cvec && cvec != 0x200)
cvec -= 4; /* backup so vector => receive */
return (sizeof(struct vvreg));
}
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
vvattach(ui)
struct uba_device *ui;
{
register struct vv_softc *vs;
vs = &vv_softc[ui->ui_unit];
vs->vs_if.if_unit = ui->ui_unit;
vs->vs_if.if_name = "vv";
vs->vs_if.if_mtu = VVMTU;
vs->vs_if.if_flags = IFF_BROADCAST;
vs->vs_if.if_init = vvinit;
vs->vs_if.if_ioctl = vvioctl;
vs->vs_if.if_output = vvoutput;
vs->vs_if.if_reset = vvreset;
vs->vs_if.if_timer = 0;
vs->vs_if.if_watchdog = vvwatchdog;
vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP;
/* use flag to determine if this is proNET-80 */
vs->vs_is80 = (short)(ui->ui_flags & 01);
#if defined(VAX750)
/* don't chew up 750 bdp's */
if (cpu == VAX_750 && ui->ui_unit > 0)
vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP;
#endif
if_attach(&vs->vs_if);
}
/*
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
*/
vvreset(unit, uban)
int unit, uban;
{
register struct uba_device *ui;
if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 ||
ui->ui_ubanum != uban)
return;
printf(" vv%d", unit);
vvinit(unit);
}
/*
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
*/
vvinit(unit)
int unit;
{
register struct vv_softc *vs;
register struct uba_device *ui;
register struct vvreg *addr;
register int ubaaddr, s;
vs = &vv_softc[unit];
ui = vvinfo[unit];
if (vs->vs_if.if_addrlist == (struct ifaddr *)0)
return;
addr = (struct vvreg *)ui->ui_addr;
if ((vs->vs_if.if_flags & IFF_RUNNING) == 0 &&
if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
sizeof (struct vv_header), (int)btoc(VVMRU)) == 0) {
printf("vv%d: can't initialize, if_ubainit() failed\n", unit);
vs->vs_if.if_flags &= ~IFF_UP;
return;
}
vs->vs_if.if_flags |= IFF_RUNNING;
/*
* Now that the uba is set up, figure out our address and
* update complete our host address.
*/
if ((vs->vs_host = vvidentify(unit)) == NOHOST) {
vs->vs_if.if_flags &= ~IFF_UP;
return;
}
printf("vv%d: host %u\n", unit, vs->vs_host);
/*
* Reset the interface, and stay in the ring
*/
addr->vvocsr = VV_RST; /* take over output */
addr->vvocsr = VV_CPB; /* clear packet buffer */
addr->vvicsr = VV_RST | VV_HEN; /* take over input, */
/* keep relay closed */
DELAY(500000); /* let contacts settle */
vs->vs_init = 0; /* clear counters, etc. */
vs->vs_refused = 0;
vs->vs_timeouts = 0;
vs->vs_otimeout = 0;
vs->vs_ibadf = 0;
vs->vs_parity = 0;
vs->vs_lastx = 256; /* an invalid address */
vs->vs_lastr = 256; /* an invalid address */
/*
* Hang a receive and start any
* pending writes by faking a transmit complete.
*/
s = splimp();
ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_r.ifrw_info);
addr->vviba = (u_short)ubaaddr;
addr->vviea = (u_short)(ubaaddr >> 16);
addr->vviwc = -(VVBUFSIZE) >> 1;
addr->vvicsr = VV_IEN | VV_HEN | VV_DEN | VV_ENB;
vs->vs_oactive = 1;
vs->vs_if.if_flags |= IFF_UP;
vvxint(unit);
splx(s);
}
/*
* Do a moderately thorough self-test in all three modes. Mostly
* to keeps defective nodes off the ring, rather than to be especially
* thorough. The key issue is to detect any cable breaks before joining
* the ring. Return our node address on success, return -1 on failure.
*
*/
/* the three self-test modes */
static u_short vv_modes[] = {
VV_STE|VV_LPB, /* digital loopback */
VV_STE, /* analog loopback */
VV_HEN /* network mode */
};
vvidentify(unit)
int unit;
{
register struct vv_softc *vs;
register struct uba_device *ui;
register struct vvreg *addr;
register struct mbuf *m;
register struct vv_header *v;
register int ubaaddr;
register int i, successes, failures, waitcount;
u_short shost = NOHOST;
vs = &vv_softc[unit];
ui = vvinfo[unit];
addr = (struct vvreg *)ui->ui_addr;
/*
* Build a multicast message to identify our address
* We need do this only once, since nobody else is about to use
* the intermediate transmit buffer (ifu_w.ifrw_addr) that
* if_ubainit() aquired for us.
*/
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == NULL) {
printf("vv%d: can't initialize, m_get() failed\n", unit);
return (0);
}
m->m_next = 0;
m->m_off = MMINOFF;
m->m_len = sizeof(struct vv_header);
v = mtod(m, struct vv_header *);
v->vh_dhost = VV_BROADCAST; /* multicast destination address */
v->vh_shost = 0; /* will be overwritten with ours */
v->vh_version = RING_VERSION;
v->vh_type = RING_DIAGNOSTICS;
v->vh_info = 0;
/* map xmit message into uba, copying to intermediate buffer */
vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
/*
* For each of the modes (digital, analog, network), go through
* a self-test that requires me to send VVIDENTSUCC good packets
* in VVIDENTRETRY attempts. Use broadcast destination to find out
* who I am, then use this as my address to check my address match
* logic. Only data checked is the vh_type field.
*/
for (i = 0; i < 3; i++) {
successes = 0; /* clear successes for this mode */
failures = 0; /* and clear failures, too */
/* take over device, and leave ring */
addr->vvicsr = VV_RST;
addr->vvocsr = VV_RST;
addr->vvicsr = vv_modes[i]; /* test mode */
/*
* let the flag and token timers pop so that the init ring bit
* will be allowed to work, by waiting about 1 second
*/
DELAY(1000000L);
/*
* retry loop
*/
while ((successes < VVIDENTSUCC) && (failures < VVIDENTRETRY))
{
/* start a receive */
ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_r.ifrw_info);
addr->vvicsr = VV_RST | vv_modes[i]; /* abort last */
addr->vviba = (u_short) ubaaddr;
addr->vviea = (u_short) (ubaaddr >> 16);
addr->vviwc = -(VVBUFSIZE) >> 1;
addr->vvicsr = vv_modes[i] | VV_DEN | VV_ENB;
/* purge stale data from BDP */
if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(vs->vs_ifuba.ifu_uba,
vs->vs_ifuba.ifu_w.ifrw_bdp);
/* do a transmit */
ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_w.ifrw_info);
addr->vvocsr = VV_RST; /* abort last try */
addr->vvoba = (u_short) ubaaddr;
addr->vvoea = (u_short) (ubaaddr >> 16);
addr->vvowc = -((vs->vs_olen + 1) >> 1);
addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
/* poll receive side for completion */
DELAY(10000); /* give it a chance */
for (waitcount = 0; waitcount < 10; waitcount++) {
if (addr->vvicsr & VV_RDY)
goto gotit;
DELAY(1000);
}
failures++; /* no luck */
continue;
gotit: /* we got something--is it any good? */
if ((addr->vvicsr & (VVRERR|VV_LDE)) ||
(addr->vvocsr & (VVXERR|VV_RFS))) {
failures++;
continue;
}
/* Purge BDP before looking at received packet */
if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(vs->vs_ifuba.ifu_uba,
vs->vs_ifuba.ifu_r.ifrw_bdp);
m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header),
0, &vs->vs_if);
if (m != NULL)
m_freem(m);
v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
/* check message type, catch our node address */
if ((v->vh_type & 0xff) == RING_DIAGNOSTICS) {
if (shost == NOHOST) {
shost = v->vh_shost & 0xff;
/* send to ourself now */
((struct vv_header *)
(vs->vs_ifuba.ifu_r.ifrw_addr))
->vh_dhost = shost;
}
successes++;
} else {
failures++;
}
v->vh_type = 0; /* clear to check again */
}
if (failures >= VVIDENTRETRY)
{
printf("vv%d: failed self-test after %d tries \
in %s mode\n",
unit, VVIDENTRETRY, i == 0 ? "digital loopback" :
(i == 1 ? "analog loopback" : "network"));
printf("vv%d: icsr = %b, ocsr = %b\n",
unit, 0xffff & addr->vvicsr, VV_IBITS,
0xffff & addr->vvocsr, VV_OBITS);
addr->vvicsr = VV_RST; /* kill the sick board */
addr->vvocsr = VV_RST;
shost = NOHOST;
goto done;
}
}
done:
/* deallocate mbuf used for send packet (won't be one, anyways) */
if (vs->vs_ifuba.ifu_xtofree) {
m_freem(vs->vs_ifuba.ifu_xtofree);
vs->vs_ifuba.ifu_xtofree = 0;
}
return(shost);
}
/*
* Start or restart output on interface.
* If interface is active, this is a retransmit, so just
* restuff registers and go.
* If interface is not already active, get another datagram
* to send off of the interface queue, and map it to the interface
* before starting the output.
*/
vvstart(dev)
dev_t dev;
{
register struct uba_device *ui;
register struct vv_softc *vs;
register struct vvreg *addr;
register struct mbuf *m;
register int unit, ubaaddr, dest, s;
unit = VVUNIT(dev);
ui = vvinfo[unit];
vs = &vv_softc[unit];
if (vs->vs_oactive)
goto restart;
/*
* Not already active: dequeue another request
* and map it to the UNIBUS. If no more requests,
* just return.
*/
s = splimp();
IF_DEQUEUE(&vs->vs_if.if_snd, m);
splx(s);
if (m == NULL) {
vs->vs_oactive = 0;
return;
}
dest = mtod(m, struct vv_header *)->vh_dhost;
vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
vs->vs_lastx = dest;
restart:
/*
* Have request mapped to UNIBUS for transmission.
* Purge any stale data from this BDP, and start the output.
*
* Make sure this packet will fit in the interface.
*/
if (vs->vs_olen > VVBUFSIZE) {
printf("vv%d vs_olen: %d > VVBUFSIZE\n", unit, vs->vs_olen);
panic("vvdriver vs_olen botch");
}
vs->vs_if.if_timer = VVTIMEOUT;
vs->vs_oactive = 1;
/* ship it */
#ifdef notdef
if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
#endif
addr = (struct vvreg *)ui->ui_addr;
ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_w.ifrw_info);
addr->vvoba = (u_short) ubaaddr;
addr->vvoea = (u_short) (ubaaddr >> 16);
addr->vvowc = -((vs->vs_olen + 1) >> 1);
addr->vvowc = -((vs->vs_olen + 1) >> 1); /* extra byte is garbage */
if (addr->vvocsr & VV_NOK)
vs->vs_init++; /* count ring inits */
addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB;
}
/*
* proNET transmit interrupt
* Start another output if more data to send.
*/
vvxint(unit)
int unit;
{
register struct uba_device *ui;
register struct vv_softc *vs;
register struct vvreg *addr;
register int oc;
#ifdef QBA
splx(vv_softc[unit].vs_ipl);
#endif
ui = vvinfo[unit];
vs = &vv_softc[unit];
vs->vs_if.if_timer = 0;
addr = (struct vvreg *)ui->ui_addr;
oc = 0xffff & (addr->vvocsr);
if (vs->vs_oactive == 0) {
vvprintf("vv%d: stray interrupt vvocsr = %b\n", unit,
oc, VV_OBITS);
return;
}
/*
* we retransmit on soft error
* TODO: sort retransmits to end of queue if possible!
*/
if (oc & (VV_OPT | VV_RFS)) {
if (vs->vs_tries++ < VVRETRY) {
if (oc & VV_OPT)
vs->vs_otimeout++;
if (oc & VV_RFS) {
vs->vs_if.if_collisions++;
vs->vs_refused++;
}
vvstart(unit); /* restart this message */
return;
}
}
vs->vs_if.if_opackets++;
vs->vs_oactive = 0;
vs->vs_tries = 0;
if (oc & VVXERR) {
vs->vs_if.if_oerrors++;
vvprintf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
VV_OBITS);
}
if (vs->vs_ifuba.ifu_xtofree) {
m_freem(vs->vs_ifuba.ifu_xtofree);
vs->vs_ifuba.ifu_xtofree = 0;
}
vvstart(unit);
}
/*
* Transmit watchdog timer routine.
* This routine gets called when we lose a transmit interrupt.
* The best we can do is try to restart output.
*/
vvwatchdog(unit)
int unit;
{
register struct vv_softc *vs;
register int s;
vs = &vv_softc[unit];
vvprintf("vv%d: lost a transmit interrupt.\n", unit);
vs->vs_timeouts++;
s = splimp();
vvstart(unit);
splx(s);
}
/*
* proNET interface receiver interrupt.
* If input error just drop packet.
* Otherwise purge input buffered data path and examine
* packet to determine type. If can't determine length
* from type, then have to drop packet. Otherwise decapsulate
* packet based on type and pass to type specific higher-level
* input routine.
*/
vvrint(unit)
int unit;
{
register struct vv_softc *vs;
register struct vvreg *addr;
register struct vv_header *vv;
register struct ifqueue *inq;
register struct mbuf *m;
int ubaaddr, len, off, s;
short resid;
#ifdef QBA
splx(vv_softc[unit].vs_ipl);
#endif
vs = &vv_softc[unit];
vs->vs_if.if_ipackets++;
addr = (struct vvreg *)vvinfo[unit]->ui_addr;
/*
* Purge BDP
*/
if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
/*
* receive errors?
*/
if (addr->vvicsr & VVRERR) {
vvprintf("vv%d: receive error, vvicsr = %b\n", unit,
0xffff&(addr->vvicsr), VV_IBITS);
if (addr->vvicsr & VV_BDF)
vs->vs_ibadf++;
goto dropit;
}
/*
* parity errors?
*/
if (addr->vvicsr & VV_LDE) {
/* we don't have to clear it because the receive command */
/* writes 0 to parity bit */
vs->vs_parity++;
/*
* only on 10 megabit proNET is VV_LDE an end-to-end parity
* bit. On 80 megabit, it returns to the intended use of
* node-to-node parity. End-to-end parity errors on 80 megabit
* give VV_BDF.
*/
if (vs->vs_is80 == 0)
goto dropit;
}
/*
* Get packet length from residual word count
*
* Compute header offset if trailer protocol
*
* Pull packet off interface. Off is nonzero if packet
* has trailing header; if_rubaget will then force this header
* information to be at the front. The vh_info field
* carries the offset to the trailer data in trailer
* format packets.
*/
vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
vvtracehdr("vi", vv);
resid = addr->vviwc & 01777; /* only low 10 bits valid */
if (resid)
resid |= 0176000; /* high 6 bits are undefined */
len = ((VVBUFSIZE >> 1) + resid) << 1;
len -= sizeof(struct vv_header);
if ((addr->vvicsr & VV_DPR) || len > VVMRU || len <= 0) {
vvprintf("vv%d: len too long or short, \
len = %d, vvicsr = %b\n",
unit, len, 0xffff&(addr->vvicsr), VV_IBITS);
goto dropit;
}
/* check the protocol header version */
if (vv->vh_version != RING_VERSION) {
vvprintf("vv%d: bad protocol header version %d\n",
unit, vv->vh_version & 0xff);
goto dropit;
}
#define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off))))
if (vv->vh_type == RING_TRAILER ) {
off = ntohs((u_short)vv->vh_info);
if (off > VVMTU) {
vvprintf("vv%d: off > VVMTU, off = %d, vvicsr = %b\n",
unit, off, 0xffff&(addr->vvicsr), VV_IBITS);
goto dropit;
}
vv->vh_type = ntohs(*vvdataaddr(vv, off, u_short *));
resid = ntohs(*(vvdataaddr(vv, off+sizeof(u_short), u_short *)));
if (off + resid > len) {
vvprintf("vv%d: trailer packet too short\n", unit);
vvprintf("vv%d: off = %d, resid = %d, vvicsr = %b\n",
unit, off, resid,
0xffff&(addr->vvicsr), VV_IBITS);
goto dropit;
}
len = off + resid;
} else
off = 0;
if (len == 0) {
vvprintf("vv%d: len is zero, vvicsr = %b\n", unit,
0xffff&(addr->vvicsr), VV_IBITS);
goto dropit;
}
m = if_rubaget(&vs->vs_ifuba, len, off, &vs->vs_if);
if (m == NULL) {
vvprintf("vv%d: if_rubaget() failed, vvicsr = %b\n", unit,
0xffff&(addr->vvicsr), VV_IBITS);
goto dropit;
}
if (off) {
struct ifnet *ifp;
ifp = *(mtod(m, struct ifnet **));
m->m_off += 2 * sizeof (u_short);
m->m_len -= 2 * sizeof (u_short);
*(mtod(m, struct ifnet **)) = ifp;
}
/* Keep track of source address of this packet */
vs->vs_lastr = vv->vh_shost;
/*
* Demultiplex on packet type
*/
switch (vv->vh_type) {
#ifdef INET
case RING_IP:
schednetisr(NETISR_IP);
inq = &ipintrq;
break;
#endif
default:
vvprintf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type);
m_freem(m);
goto setup;
}
s = splimp();
if (IF_QFULL(inq)) {
IF_DROP(inq);
m_freem(m);
} else
IF_ENQUEUE(inq, m);
splx(s);
/*
* Reset for the next packet.
*/
setup:
ubaaddr = UBAI_ADDR(vs->vs_ifuba.ifu_r.ifrw_info);
addr->vviba = (u_short) ubaaddr;
addr->vviea = (u_short) (ubaaddr >> 16);
addr->vviwc = -(VVBUFSIZE) >> 1;
addr->vvicsr = VV_HEN | VV_IEN | VV_DEN | VV_ENB;
return;
/*
* Drop packet on floor -- count them!!
*/
dropit:
vs->vs_if.if_ierrors++;
goto setup;
}
/*
* proNET output routine.
* Encapsulate a packet of type family for the local net.
* Use trailer local net encapsulation if enough data in first
* packet leaves a multiple of 512 bytes of data in remainder.
*/
vvoutput(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
register struct mbuf *m;
register struct vv_header *vv;
register int off;
register int unit;
register struct vvreg *addr;
register struct vv_softc *vs;
register int s;
int type, dest, error;
m = m0;
unit = ifp->if_unit;
addr = (struct vvreg *)vvinfo[unit]->ui_addr;
vs = &vv_softc[unit];
/*
* Check to see if the input side has wedged due the UBA
* vectoring through 0.
*
* We are lower than device ipl when we enter this routine,
* so if the interface is ready with an input packet then
* an input interrupt must have slipped through the cracks.
*
* Avoid the race with an input interrupt by watching to see
* if any packets come in.
*/
s = vs->vs_if.if_ipackets;
if (addr->vvicsr & VV_RDY && s == vs->vs_if.if_ipackets) {
vvprintf("vv%d: lost a receive interrupt, icsr = %b\n",
unit, 0xffff&(addr->vvicsr), VV_IBITS);
s = splimp();
vvrint(unit);
splx(s);
}
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr))
dest = VV_BROADCAST;
else
dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr);
#ifdef LOOPBACK
if (dest == vs->vs_host && (loif.if_flags & IFF_UP))
return (looutput(&loif, m0, dst));
#endif LOOPBACK
if (dest >= 0x100) {
error = EPERM;
goto bad;
}
off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
/*
* Trailerize, if the configuration allows it.
* TODO: Need per host negotiation.
*/
if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
if (off > 0 && (off & 0x1ff) == 0 &&
m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
type = RING_TRAILER;
m->m_off -= 2 * sizeof (u_short);
m->m_len += 2 * sizeof (u_short);
*mtod(m, u_short *) = htons((short)RING_IP);
*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
goto gottrailertype;
}
type = RING_IP;
off = 0;
goto gottype;
#endif
default:
printf("vv%d: can't handle af%d\n", unit, dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
gottrailertype:
/*
* Packet to be sent as trailer: move first packet
* (control information) to end of chain.
*/
while (m->m_next)
m = m->m_next;
m->m_next = m0;
m = m0->m_next;
m0->m_next = 0;
m0 = m;
gottype:
/*
* Add local net header. If no space in first mbuf,
* allocate another.
*/
if (m->m_off > MMAXOFF ||
MMINOFF + sizeof (struct vv_header) > m->m_off) {
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == NULL) {
error = ENOBUFS;
goto bad;
}
m->m_next = m0;
m->m_off = MMINOFF;
m->m_len = sizeof (struct vv_header);
} else {
m->m_off -= sizeof (struct vv_header);
m->m_len += sizeof (struct vv_header);
}
vv = mtod(m, struct vv_header *);
vv->vh_shost = vs->vs_host;
vv->vh_dhost = dest;
vv->vh_version = RING_VERSION;
vv->vh_type = type;
vv->vh_info = htons((u_short)off);
vvtracehdr("vo", vv);
/*
* 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);
error = ENOBUFS;
goto qfull;
}
IF_ENQUEUE(&ifp->if_snd, m);
if (vs->vs_oactive == 0)
vvstart(unit);
splx(s);
return (0);
qfull:
m0 = m;
splx(s);
bad:
m_freem(m0);
return(error);
}
/*
* Process an ioctl request.
*/
vvioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
struct ifaddr *ifa = (struct ifaddr *) data;
int s = splimp(), error = 0;
switch (cmd) {
case SIOCSIFADDR:
if ((ifp->if_flags & IFF_RUNNING) == 0)
vvinit(ifp->if_unit);
/*
* Did self-test succeed?
*/
if ((ifp->if_flags & IFF_UP) == 0)
error = ENETDOWN;
else {
/*
* Attempt to check agreement of protocol address
* and board address.
*/
switch (ifa->ifa_addr.sa_family) {
case AF_INET:
if ((in_lnaof(IA_SIN(ifa)->sin_addr) & 0xff) !=
vv_softc[ifp->if_unit].vs_host)
error = EADDRNOTAVAIL;
break;
}
}
break;
default:
error = EINVAL;
}
splx(s);
return (error);
}
/*
* vvprt_hdr(s, v) print the local net header in "v"
* with title is "s"
*/
vvprt_hdr(s, v)
char *s;
register struct vv_header *v;
{
printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
s,
0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost),
0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type),
0xffff & (int)(v->vh_info));
}
#endif NVV