xref: /csrg-svn/sys/i386/isa/if_we.c (revision 49699)
141003Swilliam /*-
241003Swilliam  * Copyright (c) 1990 The Regents of the University of California.
341003Swilliam  * All rights reserved.
441003Swilliam  *
541003Swilliam  * This code is derived from software contributed to Berkeley by
641003Swilliam  * Tim L. Tucker
741003Swilliam  *
849596Swilliam  * %sccs.include.redist.c%
941003Swilliam  *
10*49699Swilliam  *	@(#)if_we.c	7.2 (Berkeley) 05/12/91
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"
2045540Sbill #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  */
3041003Swilliam #include "param.h"
3141003Swilliam #include "mbuf.h"
3241003Swilliam #include "socket.h"
3341003Swilliam #include "ioctl.h"
3441003Swilliam #include "errno.h"
3541003Swilliam #include "syslog.h"
3641003Swilliam 
3749596Swilliam #include "net/if.h"
3849596Swilliam #include "net/netisr.h"
3941003Swilliam 
4041003Swilliam #ifdef INET
4149596Swilliam #include "netinet/in.h"
4249596Swilliam #include "netinet/in_systm.h"
4349596Swilliam #include "netinet/in_var.h"
4449596Swilliam #include "netinet/ip.h"
4549596Swilliam #include "netinet/if_ether.h"
4641003Swilliam #endif
4741003Swilliam 
4841003Swilliam #ifdef NS
4949596Swilliam #include "netns/ns.h"
5049596Swilliam #include "netns/ns_if.h"
5141003Swilliam #endif
5241003Swilliam 
5349596Swilliam #include "i386/isa/if_wereg.h"
5449596Swilliam #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  */
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  */
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 
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  */
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  */
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;
38249596Swilliam 
38345540Sbill 	unit =0;
38441003Swilliam 
38541003Swilliam 	/* disable onboard interrupts, then get interrupt status */
38645540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
38745540Sbill 	wecmd.cs_ps = 0;
38845540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
38945540Sbill 	weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
39049596Swilliam loop:
39149596Swilliam 	outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte);
39241003Swilliam 
39341003Swilliam 	/* transmit error */
39445540Sbill 	if (weisr.is_txe) {
39541003Swilliam 		/* need to read these registers to clear status */
39645540Sbill 		sc->we_if.if_collisions +=
39745540Sbill 		    inb(sc->we_io_nic_addr + WD_P0_TBCR0);
39845540Sbill 		++sc->we_if.if_oerrors;
39941003Swilliam 	}
40041003Swilliam 
40141003Swilliam 	/* receiver error */
40245540Sbill 	if (weisr.is_rxe) {
40341003Swilliam 		/* need to read these registers to clear status */
40445540Sbill 		(void) inb(sc->we_io_nic_addr + 0xD);
40545540Sbill 		(void) inb(sc->we_io_nic_addr + 0xE);
40645540Sbill 		(void) inb(sc->we_io_nic_addr + 0xF);
40745540Sbill 		++sc->we_if.if_ierrors;
40841003Swilliam 	}
40941003Swilliam 
41041003Swilliam 	/* normal transmit complete */
41149596Swilliam 	if (weisr.is_ptx || weisr.is_txe)
41245540Sbill 		wetint (unit);
41341003Swilliam 
41441003Swilliam 	/* normal receive notification */
41549596Swilliam 	if (weisr.is_prx || weisr.is_rxe)
41645540Sbill 		werint (unit);
41741003Swilliam 
41841003Swilliam 	/* try to start transmit */
41949596Swilliam 	westart(&sc->we_if);
42041003Swilliam 
42141003Swilliam 	/* re-enable onboard interrupts */
42245540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
42345540Sbill 	wecmd.cs_ps = 0;
42445540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
42549596Swilliam 	outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/);
42649596Swilliam 	weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
42749596Swilliam 	if (weisr.is_byte) goto loop;
42841003Swilliam }
42941003Swilliam 
43041003Swilliam /*
43141003Swilliam  * Ethernet interface transmit interrupt.
43241003Swilliam  */
43345540Sbill wetint(unit)
43441003Swilliam 	int unit;
43541003Swilliam {
43645540Sbill 	register struct we_softc *sc = &we_softc[unit];
43741003Swilliam 
43841003Swilliam 	/*
43941003Swilliam 	 * Do some statistics (assume page zero of NIC mapped in)
44041003Swilliam 	 */
44145540Sbill 	sc->we_flags &= ~WDF_TXBUSY;
44245540Sbill 	sc->we_if.if_timer = 0;
44345540Sbill 	++sc->we_if.if_opackets;
44445540Sbill 	sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0);
44541003Swilliam }
44641003Swilliam 
44741003Swilliam /*
44841003Swilliam  * Ethernet interface receiver interrupt.
44941003Swilliam  */
45045540Sbill werint(unit)
45141003Swilliam 	int unit;
45241003Swilliam {
45345540Sbill 	register struct we_softc *sc = &we_softc[unit];
45441003Swilliam 	u_char bnry, curr;
45549596Swilliam 	long len;
45645540Sbill 	union we_command wecmd;
45745540Sbill 	struct we_ring *wer;
45841003Swilliam 
45941003Swilliam 	/*
46041003Swilliam 	 * Traverse the receive ring looking for packets to pass back.
46141003Swilliam 	 * The search is complete when we find a descriptor not in use.
46241003Swilliam 	 */
46345540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
46445540Sbill 	wecmd.cs_ps = 0;
46545540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
46645540Sbill 	bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY);
46745540Sbill 	wecmd.cs_ps = 1;
46845540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
46945540Sbill 	curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
47049596Swilliam if(Bdry)
47145540Sbill 	bnry =Bdry;
47249596Swilliam 
47341003Swilliam 	while (bnry != curr)
47441003Swilliam 	{
47541003Swilliam 		/* get pointer to this buffer header structure */
47645540Sbill 		wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8));
47741003Swilliam 
47849596Swilliam 		/* count includes CRC */
47949596Swilliam 		len = wer->we_count - 4;
48049596Swilliam 		if (len > 30 && len <= ETHERMTU+100
48149596Swilliam 			/*&& (*(char *)wer  == 1 || *(char *) wer == 0x21)*/)
48249596Swilliam 			weread(sc, (caddr_t)(wer + 1), len);
48349596Swilliam 		else printf("reject %d", len);
48441003Swilliam 
48541003Swilliam outofbufs:
48645540Sbill 		wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
48745540Sbill 		wecmd.cs_ps = 0;
48845540Sbill 		outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
48941003Swilliam 
49049596Swilliam 		/* advance on chip Boundry register */
49149596Swilliam 		if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) {
49249596Swilliam 			bnry = WD_TXBUF_SIZE;
49349596Swilliam 			outb(sc->we_io_nic_addr + WD_P0_BNRY,
49449596Swilliam 					sc->we_vmem_size / WD_PAGE_SIZE-1);
49549596Swilliam 
49649596Swilliam 		} else {
49749596Swilliam 			if (len > 30 && len <= ETHERMTU+100)
49849596Swilliam 				bnry = wer->we_next_packet;
49949596Swilliam 			else bnry = curr;
50049596Swilliam 
50149596Swilliam 			/* watch out for NIC overflow, reset Boundry if invalid */
50249596Swilliam 			if ((bnry - 1) < WD_TXBUF_SIZE) {
50349596Swilliam 		    		outb(sc->we_io_nic_addr + WD_P0_BNRY,
50449596Swilliam 					(sc->we_vmem_size / WD_PAGE_SIZE) - 1);
50549596Swilliam 				bnry = WD_TXBUF_SIZE;
50649596Swilliam 			} else
50749596Swilliam 				outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1);
50841003Swilliam 		}
50941003Swilliam 
51041003Swilliam 		/* refresh our copy of CURR */
51145540Sbill 		wecmd.cs_ps = 1;
51245540Sbill 		outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
51345540Sbill 		curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
51441003Swilliam 	}
51545540Sbill Bdry = bnry;
51641003Swilliam }
51741003Swilliam 
51849596Swilliam #ifdef shit
51941003Swilliam /*
52049596Swilliam  * Process an ioctl request.
52141003Swilliam  */
52249596Swilliam weioctl(ifp, cmd, data)
52349596Swilliam 	register struct ifnet *ifp;
52449596Swilliam 	int cmd;
52549596Swilliam 	caddr_t data;
52641003Swilliam {
52749596Swilliam 	struct we_softc *sc = &we_softc[ifp->if_unit];
52849596Swilliam 	struct ifaddr *ifa = (struct ifaddr *)data;
52949596Swilliam 	int s = splimp(), error = 0;
53041003Swilliam 
53149596Swilliam 	switch (cmd) {
53241003Swilliam 
53349596Swilliam 	case SIOCSIFADDR:
53449596Swilliam 		ifp->if_flags |= IFF_UP;
53549596Swilliam 		weinit(ifp->if_unit);
53649596Swilliam 		switch(ifa->ifa_addr->sa_family) {
53741003Swilliam #ifdef INET
53849596Swilliam 		case AF_INET:
53949596Swilliam 			((struct arpcom *)ifp)->ac_ipaddr =
54049596Swilliam 				IA_SIN(ifa)->sin_addr;
54149596Swilliam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
54249596Swilliam 			break;
54341003Swilliam #endif
54441003Swilliam #ifdef NS
54549596Swilliam 		case AF_NS:
54649596Swilliam 		    {
54749596Swilliam 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
54849596Swilliam 
54949596Swilliam 			if (ns_nullhost(*ina))
55049596Swilliam 				ina->x_host = *(union ns_host *)(sc->we_addr);
55149596Swilliam 			else
55249596Swilliam 				wesetaddr(ina->x_host.c_host, ifp->if_unit);
55349596Swilliam 			break;
55449596Swilliam 		    }
55549596Swilliam #endif
55649596Swilliam 		}
55741003Swilliam 		break;
55841003Swilliam 
55949596Swilliam 	case SIOCSIFFLAGS:
56049596Swilliam 		if (((ifp->if_flags & IFF_UP) == 0) &&
56149596Swilliam 		   (sc->we_flags & WDF_RUNNING)) {
56249596Swilliam 			westop(ifp->if_unit);
56349596Swilliam 		} else if (((ifp->if_flags & IFF_UP) == IFF_UP) &&
56449596Swilliam 		   ((sc->we_flags & WDF_RUNNING) == 0))
56549596Swilliam 			weinit(ifp->if_unit);
56641003Swilliam 		break;
56749596Swilliam 
56841003Swilliam 	default:
56949596Swilliam 		error = EINVAL;
57041003Swilliam 
57141003Swilliam 	}
57241003Swilliam 	(void) splx(s);
57341003Swilliam 	return (error);
57441003Swilliam }
57549596Swilliam #endif
57641003Swilliam 
57741003Swilliam /*
57841003Swilliam  * Process an ioctl request.
57941003Swilliam  */
58045540Sbill weioctl(ifp, cmd, data)
58141003Swilliam 	register struct ifnet *ifp;
58241003Swilliam 	int cmd;
58341003Swilliam 	caddr_t data;
58441003Swilliam {
58549596Swilliam 	register struct ifaddr *ifa = (struct ifaddr *)data;
58645540Sbill 	struct we_softc *sc = &we_softc[ifp->if_unit];
58749596Swilliam 	struct ifreq *ifr = (struct ifreq *)data;
58841003Swilliam 	int s = splimp(), error = 0;
58949596Swilliam 
59049596Swilliam 
59141003Swilliam 	switch (cmd) {
59249596Swilliam 
59341003Swilliam 	case SIOCSIFADDR:
59441003Swilliam 		ifp->if_flags |= IFF_UP;
59549596Swilliam 
59649596Swilliam 		switch (ifa->ifa_addr->sa_family) {
59741003Swilliam #ifdef INET
59841003Swilliam 		case AF_INET:
59949596Swilliam 			weinit(ifp->if_unit);	/* before arpwhohas */
60041003Swilliam 			((struct arpcom *)ifp)->ac_ipaddr =
60141003Swilliam 				IA_SIN(ifa)->sin_addr;
60241003Swilliam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
60341003Swilliam 			break;
60441003Swilliam #endif
60541003Swilliam #ifdef NS
60641003Swilliam 		case AF_NS:
60741003Swilliam 		    {
60841003Swilliam 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
60949596Swilliam 
61041003Swilliam 			if (ns_nullhost(*ina))
61149596Swilliam 				ina->x_host = *(union ns_host *)(sc->ns_addr);
61249596Swilliam 			else {
61349596Swilliam 				/*
61449596Swilliam 				 * The manual says we can't change the address
61549596Swilliam 				 * while the receiver is armed,
61649596Swilliam 				 * so reset everything
61749596Swilliam 				 */
61849596Swilliam 				ifp->if_flags &= ~IFF_RUNNING;
61949596Swilliam 				bcopy((caddr_t)ina->x_host.c_host,
62049596Swilliam 				    (caddr_t)sc->ns_addr, sizeof(sc->ns_addr));
62149596Swilliam 			}
62249596Swilliam 			weinit(ifp->if_unit); /* does ne_setaddr() */
62341003Swilliam 			break;
62441003Swilliam 		    }
62541003Swilliam #endif
62649596Swilliam 		default:
62749596Swilliam 			weinit(ifp->if_unit);
62849596Swilliam 			break;
62941003Swilliam 		}
63041003Swilliam 		break;
63141003Swilliam 
63241003Swilliam 	case SIOCSIFFLAGS:
63349596Swilliam 		if ((ifp->if_flags & IFF_UP) == 0 &&
63449596Swilliam 		    ifp->if_flags & IFF_RUNNING) {
63549596Swilliam 			ifp->if_flags &= ~IFF_RUNNING;
63645540Sbill 			westop(ifp->if_unit);
63749596Swilliam 		} else if (ifp->if_flags & IFF_UP &&
63849596Swilliam 		    (ifp->if_flags & IFF_RUNNING) == 0)
63945540Sbill 			weinit(ifp->if_unit);
64041003Swilliam 		break;
64141003Swilliam 
64249596Swilliam #ifdef notdef
64349596Swilliam 	case SIOCGHWADDR:
64449596Swilliam 		bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data,
64549596Swilliam 			sizeof(sc->sc_addr));
64649596Swilliam 		break;
64749596Swilliam #endif
64849596Swilliam 
64941003Swilliam 	default:
65041003Swilliam 		error = EINVAL;
65141003Swilliam 	}
65249596Swilliam 	splx(s);
65341003Swilliam 	return (error);
65441003Swilliam }
65541003Swilliam /*
65641003Swilliam  * set ethernet address for unit
65741003Swilliam  */
65845540Sbill wesetaddr(physaddr, unit)
65941003Swilliam 	u_char *physaddr;
66041003Swilliam 	int unit;
66141003Swilliam {
66245540Sbill 	register struct we_softc *sc = &we_softc[unit];
66341003Swilliam 	register int i;
66441003Swilliam 
66541003Swilliam 	/*
66641003Swilliam 	 * Rewrite ethernet address, and then force restart of NIC
66741003Swilliam 	 */
66841003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; i++)
66945540Sbill 		sc->we_addr[i] = physaddr[i];
67045540Sbill 	sc->we_flags &= ~WDF_RUNNING;
67145540Sbill 	weinit(unit);
67241003Swilliam }
67341003Swilliam 
67449596Swilliam #define	wedataaddr(sc, eh, off, type) \
67549596Swilliam 	((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \
67649596Swilliam 		(((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \
67749596Swilliam 		+ (sc)->we_vmem_ring: \
67849596Swilliam 		((caddr_t)((eh)+1)+(off)))
67941003Swilliam /*
68041003Swilliam  * Pass a packet to the higher levels.
68149596Swilliam  * We deal with the trailer protocol here.
68241003Swilliam  */
68349596Swilliam weread(sc, buf, len)
68445540Sbill 	register struct we_softc *sc;
68549596Swilliam 	char *buf;
68649596Swilliam 	int len;
68741003Swilliam {
68849596Swilliam 	register struct ether_header *eh;
68949596Swilliam     	struct mbuf *m, *weget();
69049596Swilliam 	int off, resid;
69149596Swilliam 
69241003Swilliam 	/*
69349596Swilliam 	 * Deal with trailer protocol: if type is trailer type
69449596Swilliam 	 * get true type from first 16-bit word past data.
69549596Swilliam 	 * Remember that type was trailer by setting off.
69641003Swilliam 	 */
69749596Swilliam 	eh = (struct ether_header *)buf;
69849596Swilliam 	eh->ether_type = ntohs((u_short)eh->ether_type);
69949596Swilliam 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
70049596Swilliam 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
70149596Swilliam 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
70249596Swilliam 		if (off >= ETHERMTU) return;		/* sanity */
70349596Swilliam 		eh->ether_type = ntohs(*wedataaddr(sc, eh, off, u_short *));
70449596Swilliam 		resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *)));
70549596Swilliam 		if (off + resid > len) return;		/* sanity */
70649596Swilliam 		len = off + resid;
70749596Swilliam 	} else	off = 0;
70841003Swilliam 
70949596Swilliam 	len -= sizeof(struct ether_header);
71049596Swilliam 	if (len <= 0) return;
71149596Swilliam 
71241003Swilliam 	/*
71349596Swilliam 	 * Pull packet off interface.  Off is nonzero if packet
71449596Swilliam 	 * has trailing header; neget will then force this header
71549596Swilliam 	 * information to be at the front, but we still have to drop
71649596Swilliam 	 * the type and length which are at the front of any trailer data.
71741003Swilliam 	 */
71849596Swilliam 	m = weget(buf, len, off, &sc->we_if, sc);
71949596Swilliam 	if (m == 0) return;
72049596Swilliam 	ether_input(&sc->we_if, eh, m);
72149596Swilliam }
72241003Swilliam 
72349596Swilliam /*
72449596Swilliam  * Supporting routines
72549596Swilliam  */
72641003Swilliam 
72749596Swilliam /*
72849596Swilliam  * Pull read data off a interface.
72949596Swilliam  * Len is length of data, with local net header stripped.
73049596Swilliam  * Off is non-zero if a trailer protocol was used, and
73149596Swilliam  * gives the offset of the trailer information.
73249596Swilliam  * We copy the trailer information and then all the normal
73349596Swilliam  * data into mbufs.  When full cluster sized units are present
73449596Swilliam  * we copy into clusters.
73549596Swilliam  */
73649596Swilliam struct mbuf *
73749596Swilliam weget(buf, totlen, off0, ifp, sc)
73849596Swilliam 	caddr_t buf;
73949596Swilliam 	int totlen, off0;
74049596Swilliam 	struct ifnet *ifp;
74149596Swilliam 	struct we_softc *sc;
74249596Swilliam {
74349596Swilliam 	struct mbuf *top, **mp, *m, *p;
74449596Swilliam 	int off = off0, len;
74549596Swilliam 	register caddr_t cp = buf;
74649596Swilliam 	char *epkt;
747*49699Swilliam 	int tc =totlen;
74841003Swilliam 
74949596Swilliam 	buf += sizeof(struct ether_header);
75049596Swilliam 	cp = buf;
75149596Swilliam 	epkt = cp + totlen;
75249596Swilliam 
75349596Swilliam 	if (off) {
75449596Swilliam 		cp += off + 2 * sizeof(u_short);
75549596Swilliam 		totlen -= 2 * sizeof(u_short);
75649596Swilliam 	}
75749596Swilliam 
75849596Swilliam 	MGETHDR(m, M_DONTWAIT, MT_DATA);
75949596Swilliam 	if (m == 0)
76049596Swilliam 		return (0);
76149596Swilliam 	m->m_pkthdr.rcvif = ifp;
76249596Swilliam 	m->m_pkthdr.len = totlen;
76349596Swilliam 	m->m_len = MHLEN;
76449596Swilliam 
76549596Swilliam 	top = 0;
76649596Swilliam 	mp = &top;
76749596Swilliam 	while (totlen > 0) {
76849596Swilliam 		if (top) {
76949596Swilliam 			MGET(m, M_DONTWAIT, MT_DATA);
77049596Swilliam 			if (m == 0) {
77149596Swilliam 				m_freem(top);
77249596Swilliam 				return (0);
77349596Swilliam 			}
77449596Swilliam 			m->m_len = MLEN;
77549596Swilliam 		}
77649596Swilliam 		len = min(totlen, epkt - cp);
77749596Swilliam 		if (len >= MINCLSIZE) {
77849596Swilliam 			MCLGET(m, M_DONTWAIT);
77949596Swilliam 			if (m->m_flags & M_EXT)
78049596Swilliam 				m->m_len = len = min(len, MCLBYTES);
78149596Swilliam 			else
78249596Swilliam 				len = m->m_len;
78349596Swilliam 		} else {
78449596Swilliam 			/*
78549596Swilliam 			 * Place initial small packet/header at end of mbuf.
78649596Swilliam 			 */
78749596Swilliam 			if (len < m->m_len) {
78849596Swilliam 				if (top == 0 && len + max_linkhdr <= m->m_len)
78949596Swilliam 					m->m_data += max_linkhdr;
79049596Swilliam 				m->m_len = len;
79149596Swilliam 			} else
79249596Swilliam 				len = m->m_len;
79349596Swilliam 		}
79441003Swilliam 
79549596Swilliam 		totlen -= len;
79649596Swilliam 		/* only do up to end of buffer */
79749596Swilliam 		if (cp+len > sc->we_vmem_end) {
79849596Swilliam 			unsigned toend = sc->we_vmem_end - cp;
79949596Swilliam 
80049596Swilliam 			bcopy(cp, mtod(m, caddr_t), toend);
80149596Swilliam 			cp = sc->we_vmem_ring;
80249596Swilliam 			bcopy(cp, mtod(m, caddr_t)+toend, len - toend);
80349596Swilliam 			cp += len - toend;
80449596Swilliam 			epkt = cp + totlen;
80549596Swilliam 		} else {
80649596Swilliam 			bcopy(cp, mtod(m, caddr_t), (unsigned)len);
80749596Swilliam 			cp += len;
80849596Swilliam 		}
80949596Swilliam 		*mp = m;
81049596Swilliam 		mp = &m->m_next;
81149596Swilliam 		if (cp == epkt) {
81249596Swilliam 			cp = buf;
81349596Swilliam 			epkt = cp + tc;
81449596Swilliam 		}
81541003Swilliam 	}
81649596Swilliam 	return (top);
81741003Swilliam }
81841003Swilliam #endif
819