📄 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, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if_vv.c 7.11 (Berkeley) 3/25/92 */#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 "sys/param.h"#include "sys/systm.h"#include "sys/mbuf.h"#include "sys/buf.h"#include "sys/time.h"#include "sys/kernel.h"#include "sys/protosw.h"#include "sys/socket.h"#include "sys/syslog.h"#include "sys/vmmac.h"#include "sys/errno.h"#include "sys/ioctl.h"#include "net/if.h"#include "net/if_types.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 "../include/pte.h"#include "../include/cpu.h"#include "../include/mtpr.h"#include "if_vv.h"#include "if_uba.h"#include "../uba/ubareg.h"#include "../uba/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 vvlog if (vs->vs_if.if_flags & IFF_DEBUG) log/* * 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 LOOPBACKextern struct ifnet loif;#endifextern wakeup();/* * 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 */ short vs_flags; /* board state: */#define VS_RUNNING 0x01 /* board has been initialized */#define VS_INIT 0x02 /* board being initialized */} vv_softc[NVV];#define NOHOST 0xff /* 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 */ if (vs->vs_is80 = (short)(ui->ui_flags & 01)) { vs->vs_if.if_type = IFT_P80; vs->vs_if.if_baudrate = 80 * 1024 * 1024; } else { vs->vs_if.if_type = IFT_P10; vs->vs_if.if_baudrate = 10 * 1024 * 1024; } vs->vs_host = NOHOST;#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); vv_softc[unit].vs_if.if_flags &= ~IFF_RUNNING; vv_softc[unit].vs_flags &= ~VS_RUNNING; vvinit(unit, 0);}/* * Initialization of interface; clear recorded pending * operations, and reinitialize UNIBUS usage. */vvinit(unit, cansleep) int unit, cansleep;{ 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; /* * Prevent multiple instances of vvinit * from trying simultaneously. */ while (vs->vs_flags & VS_INIT) { if (cansleep) sleep((caddr_t)vs, PZERO); else return; } if (vs->vs_flags & VS_RUNNING) return; vs->vs_flags = VS_INIT; 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; vs->vs_flags = 0; 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 (cansleep) vs->vs_host = vvidentify(unit); if (vs->vs_host == NOHOST) { vs->vs_if.if_flags &= ~IFF_UP; vs->vs_flags = 0; return; } vvlog(LOG_DEBUG, "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 */ if (cansleep) { timeout(wakeup, (caddr_t)vs, hz/2); sleep((caddr_t)vs, PZERO); /* let contacts settle */ } else 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; vs->vs_flags = VS_RUNNING; /* clear VS_INIT */ wakeup((caddr_t)vs); 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. */ MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == NULL) { printf("vv%d: can't initialize, m_get() failed\n", unit); return (NOHOST); } m->m_pkthdr.len = 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 */ timeout(wakeup, (caddr_t)vs, hz); sleep((caddr_t)vs, PZERO); /* * 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;#ifdef notdef /* 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);#endif /* 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);#ifdef notdef m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0, &vs->vs_if); if (m != NULL) m_freem(m);#endif 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; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -