141003Swilliam /*-
2*63364Sbostic * Copyright (c) 1990, 1993
3*63364Sbostic * The Regents of the University of California. All rights reserved.
441003Swilliam *
541003Swilliam * This code is derived from software contributed to Berkeley by
649838Sbostic * Tim L. Tucker.
741003Swilliam *
849596Swilliam * %sccs.include.redist.c%
941003Swilliam *
10*63364Sbostic * @(#)if_we.c 8.1 (Berkeley) 06/11/93
1141003Swilliam */
1241003Swilliam
1341003Swilliam /*
1441003Swilliam * Modification history
1541003Swilliam *
1645540Sbill * 8/28/89 - Initial version(if_wd.c), Tim L Tucker
1741003Swilliam */
1841003Swilliam
1945540Sbill #include "we.h"
2056513Sbostic #if NWE > 0
2141003Swilliam /*
2241003Swilliam * Western Digital 8003 ethernet/starlan adapter
2341003Swilliam *
2441003Swilliam * Supports the following interface cards:
2545541Sbill * WD8003E, WD8003EBT, WD8003S, WD8003SBT, WD8013EBT
2641003Swilliam *
2741003Swilliam * The Western Digital card is one of many AT/MCA ethernet interfaces
2845541Sbill * based on the National DS8390 Network Interface chip set.
2941003Swilliam */
3056513Sbostic #include <sys/param.h>
3156513Sbostic #include <sys/mbuf.h>
3256513Sbostic #include <sys/socket.h>
3356513Sbostic #include <sys/ioctl.h>
3456513Sbostic #include <sys/errno.h>
3556513Sbostic #include <sys/syslog.h>
3641003Swilliam
3756513Sbostic #include <net/if.h>
3856513Sbostic #include <net/netisr.h>
3941003Swilliam
4041003Swilliam #ifdef INET
4156513Sbostic #include <netinet/in.h>
4256513Sbostic #include <netinet/in_systm.h>
4356513Sbostic #include <netinet/in_var.h>
4456513Sbostic #include <netinet/ip.h>
4556513Sbostic #include <netinet/if_ether.h>
4641003Swilliam #endif
4741003Swilliam
4841003Swilliam #ifdef NS
4956513Sbostic #include <netns/ns.h>
5056513Sbostic #include <netns/ns_if.h>
5141003Swilliam #endif
5241003Swilliam
5356513Sbostic #include <i386/isa/if_wereg.h>
5456513Sbostic #include <i386/isa/isa_device.h>
5541003Swilliam
5641003Swilliam /*
5745540Sbill * This constant should really be 60 because the we adds 4 bytes of crc.
5841003Swilliam * However when set to 60 our packets are ignored by deuna's , 3coms are
5941003Swilliam * okay ??????????????????????????????????????????
6041003Swilliam */
6141003Swilliam #define ETHER_MIN_LEN 64
6241003Swilliam #define ETHER_ADDR_LEN 6
6341003Swilliam #define ETHER_HDR_SIZE 14
6441003Swilliam
6541003Swilliam /*
6641003Swilliam * Ethernet software status per interface.
6741003Swilliam *
6841003Swilliam * Each interface is referenced by a network interface structure,
6941003Swilliam * qe_if, which the routing code uses to locate the interface.
7041003Swilliam * This structure contains the output queue for the interface, its address, ...
7141003Swilliam */
7245540Sbill struct we_softc {
7345540Sbill struct arpcom we_ac; /* Ethernet common part */
7445540Sbill #define we_if we_ac.ac_if /* network-visible interface */
7545540Sbill #define we_addr we_ac.ac_enaddr /* hardware Ethernet address */
7641003Swilliam
7745540Sbill u_char we_flags; /* software state */
7841003Swilliam #define WDF_RUNNING 0x01
7941003Swilliam #define WDF_TXBUSY 0x02
8041003Swilliam
8145540Sbill u_char we_type; /* interface type code */
8245540Sbill u_short we_vector; /* interrupt vector */
8349596Swilliam short we_io_ctl_addr; /* i/o bus address, control */
8449596Swilliam short we_io_nic_addr; /* i/o bus address, DS8390 */
8541003Swilliam
8645540Sbill caddr_t we_vmem_addr; /* card RAM virtual memory base */
8745540Sbill u_long we_vmem_size; /* card RAM bytes */
8845540Sbill caddr_t we_vmem_ring; /* receive ring RAM vaddress */
8949596Swilliam caddr_t we_vmem_end; /* receive ring RAM end */
9045540Sbill } we_softc[NWE];
9141003Swilliam
9249596Swilliam int weprobe(), weattach(), weintr(), westart();
9349596Swilliam int weinit(), ether_output(), weioctl(), wereset(), wewatchdog();
9445540Sbill
9545540Sbill struct isa_driver wedriver = {
9645540Sbill weprobe, weattach, "we",
9745540Sbill };
9841003Swilliam
9941003Swilliam /*
10041003Swilliam * Probe the WD8003 to see if it's there
10141003Swilliam */
10245540Sbill weprobe(is)
10341003Swilliam struct isa_device *is;
10441003Swilliam {
10541003Swilliam register int i;
10645540Sbill register struct we_softc *sc = &we_softc[is->id_unit];
10745540Sbill union we_mem_sel wem;
10841003Swilliam u_char sum;
10941003Swilliam
11041003Swilliam /*
11141003Swilliam * Here we check the card ROM, if the checksum passes, and the
11241003Swilliam * type code and ethernet address check out, then we know we have
11341003Swilliam * a wd8003 card.
11441003Swilliam *
11541003Swilliam * Autoconfiguration: No warning message is printed on error.
11641003Swilliam */
11741003Swilliam for (sum = 0, i = 0; i < 8; ++i)
11845540Sbill sum += inb(is->id_iobase + WD_ROM_OFFSET + i);
11941003Swilliam if (sum != WD_CHECKSUM)
12041003Swilliam return (0);
12145540Sbill sc->we_type = inb(is->id_iobase + WD_ROM_OFFSET + 6);
12245540Sbill if ((sc->we_type != WD_ETHER) && (sc->we_type != WD_STARLAN)
12345540Sbill && (sc->we_type != WD_ETHER2))
12441003Swilliam return (0);
12541003Swilliam
12641003Swilliam /*
12741003Swilliam * Setup card RAM area and i/o addresses
12841003Swilliam * Kernel Virtual to segment C0000-DFFFF?????
12941003Swilliam */
13045540Sbill sc->we_io_ctl_addr = is->id_iobase;
13145540Sbill sc->we_io_nic_addr = sc->we_io_ctl_addr + WD_NIC_OFFSET;
13245540Sbill sc->we_vector = is->id_irq;
13345540Sbill sc->we_vmem_addr = (caddr_t)is->id_maddr;
13445540Sbill sc->we_vmem_size = is->id_msize;
13545540Sbill sc->we_vmem_ring = sc->we_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE);
13649596Swilliam sc->we_vmem_end = sc->we_vmem_addr + is->id_msize;
13741003Swilliam
13841003Swilliam /*
13941003Swilliam * Save board ROM station address
14041003Swilliam */
14141003Swilliam for (i = 0; i < ETHER_ADDR_LEN; ++i)
14245540Sbill sc->we_addr[i] = inb(sc->we_io_ctl_addr + WD_ROM_OFFSET + i);
14341003Swilliam
14441003Swilliam /*
14541003Swilliam * Mapin interface memory, setup memory select register
14641003Swilliam */
14749596Swilliam /* wem.ms_addr = (u_long)sc->we_vmem_addr >> 13; */
14849596Swilliam wem.ms_addr = (u_long)(0xd0000)>> 13;
14945540Sbill wem.ms_enable = 1;
15045540Sbill wem.ms_reset = 0;
15145540Sbill outb(sc->we_io_ctl_addr, wem.ms_byte);
15241003Swilliam
15341003Swilliam /*
15441003Swilliam * clear interface memory, then sum to make sure its valid
15541003Swilliam */
15645540Sbill for (i = 0; i < sc->we_vmem_size; ++i)
15745540Sbill sc->we_vmem_addr[i] = 0x0;
15845540Sbill for (sum = 0, i = 0; i < sc->we_vmem_size; ++i)
15945540Sbill sum += sc->we_vmem_addr[i];
16041003Swilliam if (sum != 0x0) {
16145540Sbill printf("we%d: wd8003 dual port RAM address error\n", is->id_unit);
16241003Swilliam return (0);
16341003Swilliam }
16441003Swilliam
16541003Swilliam return (WD_IO_PORTS);
16641003Swilliam }
16741003Swilliam
16841003Swilliam /*
16941003Swilliam * Interface exists: make available by filling in network interface
17041003Swilliam * record. System will initialize the interface when it is ready
17141003Swilliam * to accept packets.
17241003Swilliam */
17345540Sbill weattach(is)
17441003Swilliam struct isa_device *is;
17541003Swilliam {
17645540Sbill register struct we_softc *sc = &we_softc[is->id_unit];
17745540Sbill register struct ifnet *ifp = &sc->we_if;
17849596Swilliam union we_command wecmd;
17941003Swilliam
18049596Swilliam wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
18149596Swilliam wecmd.cs_stp = 1;
18249596Swilliam wecmd.cs_sta = 0;
18349596Swilliam wecmd.cs_ps = 0;
18449596Swilliam outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
18541003Swilliam /*
18641003Swilliam * Initialize ifnet structure
18741003Swilliam */
18845540Sbill ifp->if_unit = is->id_unit;
18949596Swilliam ifp->if_name = "we" ;
19041003Swilliam ifp->if_mtu = ETHERMTU;
19149596Swilliam ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ;
19245540Sbill ifp->if_init = weinit;
19349596Swilliam ifp->if_output = ether_output;
19449596Swilliam ifp->if_start = westart;
19545540Sbill ifp->if_ioctl = weioctl;
19645540Sbill ifp->if_reset = wereset;
19749596Swilliam ifp->if_watchdog = wewatchdog;
19841003Swilliam if_attach(ifp);
19941003Swilliam
20041003Swilliam /*
20141003Swilliam * Banner...
20241003Swilliam */
20345540Sbill printf(" %s address %s",
20445540Sbill ((sc->we_type != WD_STARLAN) ? "ethernet" : "starlan"),
20545540Sbill ether_sprintf(sc->we_addr));
20641003Swilliam }
20741003Swilliam
20841003Swilliam /*
20941003Swilliam * Reset of interface.
21041003Swilliam */
wereset(unit,uban)21145540Sbill wereset(unit, uban)
21241003Swilliam int unit, uban;
21341003Swilliam {
21445540Sbill if (unit >= NWE)
21541003Swilliam return;
21645540Sbill printf("we%d: reset\n", unit);
21749596Swilliam /* we_softc[unit].we_flags &= ~WDF_RUNNING; */
21845540Sbill weinit(unit);
21941003Swilliam }
22041003Swilliam
22141003Swilliam /*
22241003Swilliam * Take interface offline.
22341003Swilliam */
westop(unit)22445540Sbill westop(unit)
22541003Swilliam int unit;
22641003Swilliam {
22745540Sbill register struct we_softc *sc = &we_softc[unit];
22845540Sbill union we_command wecmd;
22941003Swilliam int s;
23041003Swilliam
23141003Swilliam /*
23245541Sbill * Shutdown DS8390
23341003Swilliam */
23441003Swilliam s = splimp();
23545540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
23645540Sbill wecmd.cs_stp = 1;
23745540Sbill wecmd.cs_sta = 0;
23845540Sbill wecmd.cs_ps = 0;
23945540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
24041003Swilliam (void) splx(s);
24141003Swilliam }
24241003Swilliam
wewatchdog(unit)24349596Swilliam wewatchdog(unit) {
24449596Swilliam
24549596Swilliam log(LOG_WARNING,"we%d: soft reset\n", unit);
24649596Swilliam westop(unit);
24749596Swilliam weinit(unit);
24849596Swilliam }
24949596Swilliam
25049596Swilliam static Bdry;
25141003Swilliam /*
25245541Sbill * Initialization of interface (really just DS8390).
25341003Swilliam */
weinit(unit)25445540Sbill weinit(unit)
25541003Swilliam int unit;
25641003Swilliam {
25745540Sbill register struct we_softc *sc = &we_softc[unit];
25845540Sbill register struct ifnet *ifp = &sc->we_if;
25945540Sbill union we_command wecmd;
26041003Swilliam int i, s;
26141003Swilliam
26241003Swilliam /* address not known */
26341003Swilliam if (ifp->if_addrlist == (struct ifaddr *)0)
26441003Swilliam return;
26541003Swilliam
26641003Swilliam /* already running */
26749596Swilliam /*if (ifp->if_flags & IFF_RUNNING) return; */
26841003Swilliam
26941003Swilliam /*
27045541Sbill * Initialize DS8390 in order given in NSC NIC manual.
27141003Swilliam * this is stock code...please see the National manual for details.
27241003Swilliam */
27345540Sbill s = splhigh();
27449596Swilliam Bdry = 0;
27545540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
27645540Sbill wecmd.cs_stp = 1;
27745540Sbill wecmd.cs_sta = 0;
27845540Sbill wecmd.cs_ps = 0;
27945540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
28045540Sbill outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG);
28145540Sbill outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0);
28245540Sbill outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0);
28345540Sbill outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON);
28445540Sbill outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG);
28545540Sbill outb(sc->we_io_nic_addr + WD_P0_TPSR, 0);
28645540Sbill outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE);
28745540Sbill outb(sc->we_io_nic_addr + WD_P0_PSTOP,
28845540Sbill sc->we_vmem_size / WD_PAGE_SIZE);
28945540Sbill outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE);
29045540Sbill outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff);
29145540Sbill outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
29245540Sbill wecmd.cs_ps = 1;
29345540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
29441003Swilliam for (i = 0; i < ETHER_ADDR_LEN; ++i)
29545540Sbill outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]);
29641003Swilliam for (i = 0; i < ETHER_ADDR_LEN; ++i) /* == broadcast addr */
29745540Sbill outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff);
29845540Sbill outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE);
29945540Sbill wecmd.cs_ps = 0;
30045540Sbill wecmd.cs_stp = 0;
30145540Sbill wecmd.cs_sta = 1;
30245540Sbill wecmd.cs_rd = 0x4;
30345540Sbill outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte);
30445540Sbill outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG);
30541003Swilliam
30641003Swilliam /*
30741003Swilliam * Take the interface out of reset, program the vector,
30841003Swilliam * enable interrupts, and tell the world we are up.
30941003Swilliam */
31049596Swilliam ifp->if_flags |= IFF_RUNNING;
31145540Sbill sc->we_flags &= ~WDF_TXBUSY;
31241003Swilliam (void) splx(s);
31349596Swilliam westart(ifp);
31441003Swilliam }
31541003Swilliam
31641003Swilliam /*
31741003Swilliam * Start output on interface.
31841003Swilliam */
31949596Swilliam westart(ifp)
32049596Swilliam struct ifnet *ifp;
32141003Swilliam {
32249596Swilliam register struct we_softc *sc = &we_softc[ifp->if_unit];
32341003Swilliam struct mbuf *m0, *m;
32441003Swilliam register caddr_t buffer;
32549596Swilliam int len, s;
32645540Sbill union we_command wecmd;
32741003Swilliam
32841003Swilliam /*
32945541Sbill * The DS8390 has only one transmit buffer, if it is busy we
33041003Swilliam * must wait until the transmit interrupt completes.
33141003Swilliam */
33245540Sbill s = splhigh();
33345540Sbill if (sc->we_flags & WDF_TXBUSY) {
33441003Swilliam (void) splx(s);
33541003Swilliam return;
33641003Swilliam }
33745540Sbill IF_DEQUEUE(&sc->we_if.if_snd, m);
33841003Swilliam if (m == 0) {
33941003Swilliam (void) splx(s);
34041003Swilliam return;
34141003Swilliam }
34245540Sbill sc->we_flags |= WDF_TXBUSY;
34341003Swilliam (void) splx(s);
34441003Swilliam
34541003Swilliam /*
34641003Swilliam * Copy the mbuf chain into the transmit buffer
34741003Swilliam */
34845540Sbill buffer = sc->we_vmem_addr;
34949596Swilliam len = 0;
35041003Swilliam for (m0 = m; m != 0; m = m->m_next) {
35141003Swilliam bcopy(mtod(m, caddr_t), buffer, m->m_len);
35241003Swilliam buffer += m->m_len;
35341003Swilliam len += m->m_len;
35441003Swilliam }
35541003Swilliam
35649596Swilliam m_freem(m0);
35741003Swilliam
35841003Swilliam /*
35941003Swilliam * Init transmit length registers, and set transmit start flag.
36041003Swilliam */
36145540Sbill s = splhigh();
36241003Swilliam len = MAX(len, ETHER_MIN_LEN);
36345540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
36445540Sbill wecmd.cs_ps = 0;
36545540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
36645540Sbill outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff);
36745540Sbill outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8);
36845540Sbill wecmd.cs_txp = 1;
36945540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
37041003Swilliam (void) splx(s);
37141003Swilliam }
37241003Swilliam
37341003Swilliam /*
37441003Swilliam * Ethernet interface interrupt processor
37541003Swilliam */
weintr(unit)37645540Sbill weintr(unit)
37741003Swilliam int unit;
37841003Swilliam {
37949596Swilliam register struct we_softc *sc = &we_softc[unit];
38045540Sbill union we_command wecmd;
38145540Sbill union we_interrupt weisr;
38253641Sbostic int nloops = 10;
38349596Swilliam
38445540Sbill unit =0;
38541003Swilliam
38641003Swilliam /* disable onboard interrupts, then get interrupt status */
38745540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
38845540Sbill wecmd.cs_ps = 0;
38945540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
39045540Sbill weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
39149596Swilliam loop:
39249596Swilliam outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte);
39341003Swilliam
39441003Swilliam /* transmit error */
39545540Sbill if (weisr.is_txe) {
39641003Swilliam /* need to read these registers to clear status */
39745540Sbill sc->we_if.if_collisions +=
39845540Sbill inb(sc->we_io_nic_addr + WD_P0_TBCR0);
39945540Sbill ++sc->we_if.if_oerrors;
40041003Swilliam }
40141003Swilliam
40241003Swilliam /* receiver error */
40345540Sbill if (weisr.is_rxe) {
40441003Swilliam /* need to read these registers to clear status */
40545540Sbill (void) inb(sc->we_io_nic_addr + 0xD);
40645540Sbill (void) inb(sc->we_io_nic_addr + 0xE);
40745540Sbill (void) inb(sc->we_io_nic_addr + 0xF);
40845540Sbill ++sc->we_if.if_ierrors;
40941003Swilliam }
41041003Swilliam
41141003Swilliam /* normal transmit complete */
41249596Swilliam if (weisr.is_ptx || weisr.is_txe)
41345540Sbill wetint (unit);
41441003Swilliam
41541003Swilliam /* normal receive notification */
41649596Swilliam if (weisr.is_prx || weisr.is_rxe)
41745540Sbill werint (unit);
41841003Swilliam
41941003Swilliam /* try to start transmit */
42049596Swilliam westart(&sc->we_if);
42141003Swilliam
42241003Swilliam /* re-enable onboard interrupts */
42345540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
42445540Sbill wecmd.cs_ps = 0;
42545540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
42649596Swilliam outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/);
42749596Swilliam weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
42853641Sbostic if (weisr.is_byte) {
42953641Sbostic /*
43053641Sbostic * I caught it looping forever here a couple of times,
43153641Sbostic * but I haven't had time to figure out why. Just
43253641Sbostic * returning seems to be safe, and it does not appear
43353641Sbostic * to interfere with future packets. - Pace 5/19/92
43453641Sbostic */
43553641Sbostic if (--nloops <= 0) {
43653641Sbostic printf ("we0: weintr is looping\n");
43753641Sbostic return;
43853641Sbostic }
43953641Sbostic goto loop;
44053641Sbostic }
44141003Swilliam }
44241003Swilliam
44341003Swilliam /*
44441003Swilliam * Ethernet interface transmit interrupt.
44541003Swilliam */
wetint(unit)44645540Sbill wetint(unit)
44741003Swilliam int unit;
44841003Swilliam {
44945540Sbill register struct we_softc *sc = &we_softc[unit];
45041003Swilliam
45141003Swilliam /*
45241003Swilliam * Do some statistics (assume page zero of NIC mapped in)
45341003Swilliam */
45445540Sbill sc->we_flags &= ~WDF_TXBUSY;
45545540Sbill sc->we_if.if_timer = 0;
45645540Sbill ++sc->we_if.if_opackets;
45745540Sbill sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0);
45841003Swilliam }
45941003Swilliam
46041003Swilliam /*
46141003Swilliam * Ethernet interface receiver interrupt.
46241003Swilliam */
werint(unit)46345540Sbill werint(unit)
46441003Swilliam int unit;
46541003Swilliam {
46645540Sbill register struct we_softc *sc = &we_softc[unit];
46741003Swilliam u_char bnry, curr;
46849596Swilliam long len;
46945540Sbill union we_command wecmd;
47045540Sbill struct we_ring *wer;
47141003Swilliam
47241003Swilliam /*
47341003Swilliam * Traverse the receive ring looking for packets to pass back.
47441003Swilliam * The search is complete when we find a descriptor not in use.
47541003Swilliam */
47645540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
47745540Sbill wecmd.cs_ps = 0;
47845540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
47945540Sbill bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY);
48045540Sbill wecmd.cs_ps = 1;
48145540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
48245540Sbill curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
48349596Swilliam if(Bdry)
48445540Sbill bnry =Bdry;
48549596Swilliam
48641003Swilliam while (bnry != curr)
48741003Swilliam {
48841003Swilliam /* get pointer to this buffer header structure */
48945540Sbill wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8));
49041003Swilliam
49149596Swilliam /* count includes CRC */
49249596Swilliam len = wer->we_count - 4;
49349596Swilliam if (len > 30 && len <= ETHERMTU+100
49449596Swilliam /*&& (*(char *)wer == 1 || *(char *) wer == 0x21)*/)
49549596Swilliam weread(sc, (caddr_t)(wer + 1), len);
49649596Swilliam else printf("reject %d", len);
49741003Swilliam
49841003Swilliam outofbufs:
49945540Sbill wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
50045540Sbill wecmd.cs_ps = 0;
50145540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
50241003Swilliam
50349596Swilliam /* advance on chip Boundry register */
50449596Swilliam if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) {
50549596Swilliam bnry = WD_TXBUF_SIZE;
50649596Swilliam outb(sc->we_io_nic_addr + WD_P0_BNRY,
50749596Swilliam sc->we_vmem_size / WD_PAGE_SIZE-1);
50849596Swilliam
50949596Swilliam } else {
51049596Swilliam if (len > 30 && len <= ETHERMTU+100)
51149596Swilliam bnry = wer->we_next_packet;
51249596Swilliam else bnry = curr;
51349596Swilliam
51449596Swilliam /* watch out for NIC overflow, reset Boundry if invalid */
51549596Swilliam if ((bnry - 1) < WD_TXBUF_SIZE) {
51649596Swilliam outb(sc->we_io_nic_addr + WD_P0_BNRY,
51749596Swilliam (sc->we_vmem_size / WD_PAGE_SIZE) - 1);
51849596Swilliam bnry = WD_TXBUF_SIZE;
51949596Swilliam } else
52049596Swilliam outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1);
52141003Swilliam }
52241003Swilliam
52341003Swilliam /* refresh our copy of CURR */
52445540Sbill wecmd.cs_ps = 1;
52545540Sbill outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
52645540Sbill curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
52741003Swilliam }
52845540Sbill Bdry = bnry;
52941003Swilliam }
53041003Swilliam
53149596Swilliam #ifdef shit
53241003Swilliam /*
53349596Swilliam * Process an ioctl request.
53441003Swilliam */
weioctl(ifp,cmd,data)53549596Swilliam weioctl(ifp, cmd, data)
53649596Swilliam register struct ifnet *ifp;
53749596Swilliam int cmd;
53849596Swilliam caddr_t data;
53941003Swilliam {
54049596Swilliam struct we_softc *sc = &we_softc[ifp->if_unit];
54149596Swilliam struct ifaddr *ifa = (struct ifaddr *)data;
54249596Swilliam int s = splimp(), error = 0;
54341003Swilliam
54449596Swilliam switch (cmd) {
54541003Swilliam
54649596Swilliam case SIOCSIFADDR:
54749596Swilliam ifp->if_flags |= IFF_UP;
54849596Swilliam weinit(ifp->if_unit);
54949596Swilliam switch(ifa->ifa_addr->sa_family) {
55041003Swilliam #ifdef INET
55149596Swilliam case AF_INET:
55249596Swilliam ((struct arpcom *)ifp)->ac_ipaddr =
55349596Swilliam IA_SIN(ifa)->sin_addr;
55449596Swilliam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
55549596Swilliam break;
55641003Swilliam #endif
55741003Swilliam #ifdef NS
55849596Swilliam case AF_NS:
55949596Swilliam {
56049596Swilliam register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
56149596Swilliam
56249596Swilliam if (ns_nullhost(*ina))
56349596Swilliam ina->x_host = *(union ns_host *)(sc->we_addr);
56449596Swilliam else
56549596Swilliam wesetaddr(ina->x_host.c_host, ifp->if_unit);
56649596Swilliam break;
56749596Swilliam }
56849596Swilliam #endif
56949596Swilliam }
57041003Swilliam break;
57141003Swilliam
57249596Swilliam case SIOCSIFFLAGS:
57349596Swilliam if (((ifp->if_flags & IFF_UP) == 0) &&
57449596Swilliam (sc->we_flags & WDF_RUNNING)) {
57549596Swilliam westop(ifp->if_unit);
57649596Swilliam } else if (((ifp->if_flags & IFF_UP) == IFF_UP) &&
57749596Swilliam ((sc->we_flags & WDF_RUNNING) == 0))
57849596Swilliam weinit(ifp->if_unit);
57941003Swilliam break;
58049596Swilliam
58141003Swilliam default:
58249596Swilliam error = EINVAL;
58341003Swilliam
58441003Swilliam }
58541003Swilliam (void) splx(s);
58641003Swilliam return (error);
58741003Swilliam }
58849596Swilliam #endif
58941003Swilliam
59041003Swilliam /*
59141003Swilliam * Process an ioctl request.
59241003Swilliam */
weioctl(ifp,cmd,data)59345540Sbill weioctl(ifp, cmd, data)
59441003Swilliam register struct ifnet *ifp;
59541003Swilliam int cmd;
59641003Swilliam caddr_t data;
59741003Swilliam {
59849596Swilliam register struct ifaddr *ifa = (struct ifaddr *)data;
59945540Sbill struct we_softc *sc = &we_softc[ifp->if_unit];
60049596Swilliam struct ifreq *ifr = (struct ifreq *)data;
60141003Swilliam int s = splimp(), error = 0;
60249596Swilliam
60349596Swilliam
60441003Swilliam switch (cmd) {
60549596Swilliam
60641003Swilliam case SIOCSIFADDR:
60741003Swilliam ifp->if_flags |= IFF_UP;
60849596Swilliam
60949596Swilliam switch (ifa->ifa_addr->sa_family) {
61041003Swilliam #ifdef INET
61141003Swilliam case AF_INET:
61249596Swilliam weinit(ifp->if_unit); /* before arpwhohas */
61341003Swilliam ((struct arpcom *)ifp)->ac_ipaddr =
61441003Swilliam IA_SIN(ifa)->sin_addr;
61541003Swilliam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
61641003Swilliam break;
61741003Swilliam #endif
61841003Swilliam #ifdef NS
61941003Swilliam case AF_NS:
62041003Swilliam {
62141003Swilliam register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
62249596Swilliam
62341003Swilliam if (ns_nullhost(*ina))
62449596Swilliam ina->x_host = *(union ns_host *)(sc->ns_addr);
62549596Swilliam else {
62649596Swilliam /*
62749596Swilliam * The manual says we can't change the address
62849596Swilliam * while the receiver is armed,
62949596Swilliam * so reset everything
63049596Swilliam */
63149596Swilliam ifp->if_flags &= ~IFF_RUNNING;
63249596Swilliam bcopy((caddr_t)ina->x_host.c_host,
63349596Swilliam (caddr_t)sc->ns_addr, sizeof(sc->ns_addr));
63449596Swilliam }
63549596Swilliam weinit(ifp->if_unit); /* does ne_setaddr() */
63641003Swilliam break;
63741003Swilliam }
63841003Swilliam #endif
63949596Swilliam default:
64049596Swilliam weinit(ifp->if_unit);
64149596Swilliam break;
64241003Swilliam }
64341003Swilliam break;
64441003Swilliam
64541003Swilliam case SIOCSIFFLAGS:
64649596Swilliam if ((ifp->if_flags & IFF_UP) == 0 &&
64749596Swilliam ifp->if_flags & IFF_RUNNING) {
64849596Swilliam ifp->if_flags &= ~IFF_RUNNING;
64945540Sbill westop(ifp->if_unit);
65049596Swilliam } else if (ifp->if_flags & IFF_UP &&
65149596Swilliam (ifp->if_flags & IFF_RUNNING) == 0)
65245540Sbill weinit(ifp->if_unit);
65341003Swilliam break;
65441003Swilliam
65549596Swilliam #ifdef notdef
65649596Swilliam case SIOCGHWADDR:
65749596Swilliam bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data,
65849596Swilliam sizeof(sc->sc_addr));
65949596Swilliam break;
66049596Swilliam #endif
66149596Swilliam
66241003Swilliam default:
66341003Swilliam error = EINVAL;
66441003Swilliam }
66549596Swilliam splx(s);
66641003Swilliam return (error);
66741003Swilliam }
66841003Swilliam /*
66941003Swilliam * set ethernet address for unit
67041003Swilliam */
wesetaddr(physaddr,unit)67145540Sbill wesetaddr(physaddr, unit)
67241003Swilliam u_char *physaddr;
67341003Swilliam int unit;
67441003Swilliam {
67545540Sbill register struct we_softc *sc = &we_softc[unit];
67641003Swilliam register int i;
67741003Swilliam
67841003Swilliam /*
67941003Swilliam * Rewrite ethernet address, and then force restart of NIC
68041003Swilliam */
68141003Swilliam for (i = 0; i < ETHER_ADDR_LEN; i++)
68245540Sbill sc->we_addr[i] = physaddr[i];
68345540Sbill sc->we_flags &= ~WDF_RUNNING;
68445540Sbill weinit(unit);
68541003Swilliam }
68641003Swilliam
68749596Swilliam #define wedataaddr(sc, eh, off, type) \
68849596Swilliam ((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \
68949596Swilliam (((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \
69049596Swilliam + (sc)->we_vmem_ring: \
69149596Swilliam ((caddr_t)((eh)+1)+(off)))
69241003Swilliam /*
69341003Swilliam * Pass a packet to the higher levels.
69449596Swilliam * We deal with the trailer protocol here.
69541003Swilliam */
weread(sc,buf,len)69649596Swilliam weread(sc, buf, len)
69745540Sbill register struct we_softc *sc;
69849596Swilliam char *buf;
69949596Swilliam int len;
70041003Swilliam {
70149596Swilliam register struct ether_header *eh;
70249596Swilliam struct mbuf *m, *weget();
70349596Swilliam int off, resid;
70449596Swilliam
70541003Swilliam /*
70649596Swilliam * Deal with trailer protocol: if type is trailer type
70749596Swilliam * get true type from first 16-bit word past data.
70849596Swilliam * Remember that type was trailer by setting off.
70941003Swilliam */
71049596Swilliam eh = (struct ether_header *)buf;
71149596Swilliam eh->ether_type = ntohs((u_short)eh->ether_type);
71249596Swilliam if (eh->ether_type >= ETHERTYPE_TRAIL &&
71349596Swilliam eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
71449596Swilliam off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
71549596Swilliam if (off >= ETHERMTU) return; /* sanity */
71649596Swilliam eh->ether_type = ntohs(*wedataaddr(sc, eh, off, u_short *));
71749596Swilliam resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *)));
71849596Swilliam if (off + resid > len) return; /* sanity */
71949596Swilliam len = off + resid;
72049596Swilliam } else off = 0;
72141003Swilliam
72249596Swilliam len -= sizeof(struct ether_header);
72349596Swilliam if (len <= 0) return;
72449596Swilliam
72541003Swilliam /*
72649596Swilliam * Pull packet off interface. Off is nonzero if packet
72749596Swilliam * has trailing header; neget will then force this header
72849596Swilliam * information to be at the front, but we still have to drop
72949596Swilliam * the type and length which are at the front of any trailer data.
73041003Swilliam */
73149596Swilliam m = weget(buf, len, off, &sc->we_if, sc);
73249596Swilliam if (m == 0) return;
73349596Swilliam ether_input(&sc->we_if, eh, m);
73449596Swilliam }
73541003Swilliam
73649596Swilliam /*
73749596Swilliam * Supporting routines
73849596Swilliam */
73941003Swilliam
74049596Swilliam /*
74149596Swilliam * Pull read data off a interface.
74249596Swilliam * Len is length of data, with local net header stripped.
74349596Swilliam * Off is non-zero if a trailer protocol was used, and
74449596Swilliam * gives the offset of the trailer information.
74549596Swilliam * We copy the trailer information and then all the normal
74649596Swilliam * data into mbufs. When full cluster sized units are present
74749596Swilliam * we copy into clusters.
74849596Swilliam */
74949596Swilliam struct mbuf *
weget(buf,totlen,off0,ifp,sc)75049596Swilliam weget(buf, totlen, off0, ifp, sc)
75149596Swilliam caddr_t buf;
75249596Swilliam int totlen, off0;
75349596Swilliam struct ifnet *ifp;
75449596Swilliam struct we_softc *sc;
75549596Swilliam {
75649596Swilliam struct mbuf *top, **mp, *m, *p;
75749596Swilliam int off = off0, len;
75849596Swilliam register caddr_t cp = buf;
75949596Swilliam char *epkt;
76049699Swilliam int tc =totlen;
76141003Swilliam
76249596Swilliam buf += sizeof(struct ether_header);
76349596Swilliam cp = buf;
76449596Swilliam epkt = cp + totlen;
76549596Swilliam
76649596Swilliam if (off) {
76749596Swilliam cp += off + 2 * sizeof(u_short);
76849596Swilliam totlen -= 2 * sizeof(u_short);
76949596Swilliam }
77049596Swilliam
77149596Swilliam MGETHDR(m, M_DONTWAIT, MT_DATA);
77249596Swilliam if (m == 0)
77349596Swilliam return (0);
77449596Swilliam m->m_pkthdr.rcvif = ifp;
77549596Swilliam m->m_pkthdr.len = totlen;
77649596Swilliam m->m_len = MHLEN;
77749596Swilliam
77849596Swilliam top = 0;
77949596Swilliam mp = ⊤
78049596Swilliam while (totlen > 0) {
78149596Swilliam if (top) {
78249596Swilliam MGET(m, M_DONTWAIT, MT_DATA);
78349596Swilliam if (m == 0) {
78449596Swilliam m_freem(top);
78549596Swilliam return (0);
78649596Swilliam }
78749596Swilliam m->m_len = MLEN;
78849596Swilliam }
78949596Swilliam len = min(totlen, epkt - cp);
79049596Swilliam if (len >= MINCLSIZE) {
79149596Swilliam MCLGET(m, M_DONTWAIT);
79249596Swilliam if (m->m_flags & M_EXT)
79349596Swilliam m->m_len = len = min(len, MCLBYTES);
79449596Swilliam else
79549596Swilliam len = m->m_len;
79649596Swilliam } else {
79749596Swilliam /*
79849596Swilliam * Place initial small packet/header at end of mbuf.
79949596Swilliam */
80049596Swilliam if (len < m->m_len) {
80149596Swilliam if (top == 0 && len + max_linkhdr <= m->m_len)
80249596Swilliam m->m_data += max_linkhdr;
80349596Swilliam m->m_len = len;
80449596Swilliam } else
80549596Swilliam len = m->m_len;
80649596Swilliam }
80741003Swilliam
80849596Swilliam totlen -= len;
80949596Swilliam /* only do up to end of buffer */
81049596Swilliam if (cp+len > sc->we_vmem_end) {
81149596Swilliam unsigned toend = sc->we_vmem_end - cp;
81249596Swilliam
81349596Swilliam bcopy(cp, mtod(m, caddr_t), toend);
81449596Swilliam cp = sc->we_vmem_ring;
81549596Swilliam bcopy(cp, mtod(m, caddr_t)+toend, len - toend);
81649596Swilliam cp += len - toend;
81749596Swilliam epkt = cp + totlen;
81849596Swilliam } else {
81949596Swilliam bcopy(cp, mtod(m, caddr_t), (unsigned)len);
82049596Swilliam cp += len;
82149596Swilliam }
82249596Swilliam *mp = m;
82349596Swilliam mp = &m->m_next;
82449596Swilliam if (cp == epkt) {
82549596Swilliam cp = buf;
82649596Swilliam epkt = cp + tc;
82749596Swilliam }
82841003Swilliam }
82949596Swilliam return (top);
83041003Swilliam }
83141003Swilliam #endif
832