xref: /csrg-svn/sys/i386/isa/if_we.c (revision 49596)
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  *
8*49596Swilliam  * %sccs.include.redist.c%
941003Swilliam  *
10*49596Swilliam  *	@(#)if_we.c	7.1 (Berkeley) 05/09/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 
37*49596Swilliam #include "net/if.h"
38*49596Swilliam #include "net/netisr.h"
3941003Swilliam 
4041003Swilliam #ifdef INET
41*49596Swilliam #include "netinet/in.h"
42*49596Swilliam #include "netinet/in_systm.h"
43*49596Swilliam #include "netinet/in_var.h"
44*49596Swilliam #include "netinet/ip.h"
45*49596Swilliam #include "netinet/if_ether.h"
4641003Swilliam #endif
4741003Swilliam 
4841003Swilliam #ifdef NS
49*49596Swilliam #include "netns/ns.h"
50*49596Swilliam #include "netns/ns_if.h"
5141003Swilliam #endif
5241003Swilliam 
53*49596Swilliam #include "i386/isa/if_wereg.h"
54*49596Swilliam #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 		*/
83*49596Swilliam 	short	we_io_ctl_addr;		/* i/o bus address, control	*/
84*49596Swilliam 	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	*/
89*49596Swilliam 	caddr_t	we_vmem_end;		/* receive ring RAM end	*/
9045540Sbill } we_softc[NWE];
9141003Swilliam 
92*49596Swilliam int	weprobe(), weattach(), weintr(), westart();
93*49596Swilliam 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);
136*49596Swilliam 	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 	 */
147*49596Swilliam 	/* wem.ms_addr = (u_long)sc->we_vmem_addr >> 13; */
148*49596Swilliam 	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;
178*49596Swilliam 	union we_command wecmd;
17941003Swilliam 
180*49596Swilliam 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
181*49596Swilliam 	wecmd.cs_stp = 1;
182*49596Swilliam 	wecmd.cs_sta = 0;
183*49596Swilliam 	wecmd.cs_ps = 0;
184*49596Swilliam 	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;
189*49596Swilliam 	ifp->if_name = "we" ;
19041003Swilliam 	ifp->if_mtu = ETHERMTU;
191*49596Swilliam 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ;
19245540Sbill 	ifp->if_init = weinit;
193*49596Swilliam 	ifp->if_output = ether_output;
194*49596Swilliam 	ifp->if_start = westart;
19545540Sbill 	ifp->if_ioctl = weioctl;
19645540Sbill 	ifp->if_reset = wereset;
197*49596Swilliam 	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);
217*49596Swilliam /*	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 
243*49596Swilliam wewatchdog(unit) {
244*49596Swilliam 
245*49596Swilliam 	log(LOG_WARNING,"we%d: soft reset\n", unit);
246*49596Swilliam printf("we: reset!\n");
247*49596Swilliam 	westop(unit);
248*49596Swilliam DELAY(100000);
249*49596Swilliam 	weinit(unit);
250*49596Swilliam }
251*49596Swilliam 
252*49596Swilliam static Bdry;
25341003Swilliam /*
25445541Sbill  * Initialization of interface (really just DS8390).
25541003Swilliam  */
25645540Sbill weinit(unit)
25741003Swilliam 	int unit;
25841003Swilliam {
25945540Sbill 	register struct we_softc *sc = &we_softc[unit];
26045540Sbill 	register struct ifnet *ifp = &sc->we_if;
26145540Sbill 	union we_command wecmd;
26241003Swilliam 	int i, s;
26341003Swilliam 
26441003Swilliam 	/* address not known */
26541003Swilliam 	if (ifp->if_addrlist == (struct ifaddr *)0)
26641003Swilliam 		return;
26741003Swilliam 
26841003Swilliam 	/* already running */
269*49596Swilliam /*	if (sc->we_flags & WDF_RUNNING)*/
270*49596Swilliam 	/*if (ifp->if_flags & IFF_RUNNING) return; */
27141003Swilliam 
27241003Swilliam 	/*
27345541Sbill 	 * Initialize DS8390 in order given in NSC NIC manual.
27441003Swilliam 	 * this is stock code...please see the National manual for details.
27541003Swilliam 	 */
27645540Sbill 	s = splhigh();
277*49596Swilliam Bdry = 0;
27845540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
27945540Sbill 	wecmd.cs_stp = 1;
28045540Sbill 	wecmd.cs_sta = 0;
28145540Sbill 	wecmd.cs_ps = 0;
28245540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
28345540Sbill 	outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG);
28445540Sbill 	outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0);
28545540Sbill 	outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0);
28645540Sbill 	outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON);
28745540Sbill 	outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG);
28845540Sbill 	outb(sc->we_io_nic_addr + WD_P0_TPSR, 0);
28945540Sbill 	outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE);
29045540Sbill 	outb(sc->we_io_nic_addr + WD_P0_PSTOP,
29145540Sbill 		sc->we_vmem_size / WD_PAGE_SIZE);
29245540Sbill 	outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE);
29345540Sbill 	outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff);
29445540Sbill 	outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
29545540Sbill 	wecmd.cs_ps = 1;
29645540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
29741003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; ++i)
29845540Sbill 	    outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]);
29941003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; ++i)	/* == broadcast addr */
30045540Sbill 	    outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff);
30145540Sbill 	outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE);
30245540Sbill 	wecmd.cs_ps = 0;
30345540Sbill 	wecmd.cs_stp = 0;
30445540Sbill 	wecmd.cs_sta = 1;
30545540Sbill 	wecmd.cs_rd = 0x4;
30645540Sbill 	outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte);
30745540Sbill 	outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG);
30841003Swilliam 
30941003Swilliam 	/*
31041003Swilliam 	 * Take the interface out of reset, program the vector,
31141003Swilliam 	 * enable interrupts, and tell the world we are up.
31241003Swilliam 	 */
313*49596Swilliam 	ifp->if_flags |= IFF_RUNNING;
31445540Sbill 	sc->we_flags &= ~WDF_TXBUSY;
31541003Swilliam 	(void) splx(s);
316*49596Swilliam 	westart(ifp);
31741003Swilliam }
31841003Swilliam 
31941003Swilliam /*
32041003Swilliam  * Start output on interface.
32141003Swilliam  */
322*49596Swilliam westart(ifp)
323*49596Swilliam 	struct ifnet *ifp;
32441003Swilliam {
325*49596Swilliam 	register struct we_softc *sc = &we_softc[ifp->if_unit];
32641003Swilliam 	struct mbuf *m0, *m;
32741003Swilliam 	register caddr_t buffer;
328*49596Swilliam 	int len, s;
32945540Sbill 	union we_command wecmd;
33041003Swilliam 
33141003Swilliam 	/*
33245541Sbill 	 * The DS8390 has only one transmit buffer, if it is busy we
33341003Swilliam 	 * must wait until the transmit interrupt completes.
33441003Swilliam 	 */
33545540Sbill 	s = splhigh();
33645540Sbill 	if (sc->we_flags & WDF_TXBUSY) {
33741003Swilliam 		(void) splx(s);
33841003Swilliam 		return;
33941003Swilliam 	}
34045540Sbill 	IF_DEQUEUE(&sc->we_if.if_snd, m);
34141003Swilliam 	if (m == 0) {
34241003Swilliam 		(void) splx(s);
34341003Swilliam 		return;
34441003Swilliam 	}
34545540Sbill 	sc->we_flags |= WDF_TXBUSY;
34641003Swilliam 	(void) splx(s);
34741003Swilliam 
34841003Swilliam 	/*
34941003Swilliam 	 * Copy the mbuf chain into the transmit buffer
35041003Swilliam 	 */
35145540Sbill 	buffer = sc->we_vmem_addr;
352*49596Swilliam 	len = 0;
353*49596Swilliam /*printf("\nT   "); */
35441003Swilliam 	for (m0 = m; m != 0; m = m->m_next) {
355*49596Swilliam /*int j;*/
35641003Swilliam 		bcopy(mtod(m, caddr_t), buffer, m->m_len);
357*49596Swilliam /*for(j=0; j < m->m_len;j++) {
358*49596Swilliam 
359*49596Swilliam 	puthex(buffer[j]);
360*49596Swilliam 	if (j == sizeof(struct ether_header)-1)
361*49596Swilliam 		printf("|");
362*49596Swilliam }*/
36341003Swilliam 		buffer += m->m_len;
36441003Swilliam         	len += m->m_len;
36541003Swilliam 	}
366*49596Swilliam /*printf("|%d ", len);*/
36741003Swilliam 
368*49596Swilliam 	m_freem(m0);
36941003Swilliam 
37041003Swilliam 	/*
37141003Swilliam 	 * Init transmit length registers, and set transmit start flag.
37241003Swilliam 	 */
37345540Sbill 	s = splhigh();
37441003Swilliam 	len = MAX(len, ETHER_MIN_LEN);
375*49596Swilliam /*printf("L %d ", len);
376*49596Swilliam 	if (len < 70) len=70;*/
37745540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
37845540Sbill 	wecmd.cs_ps = 0;
37945540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
38045540Sbill 	outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff);
38145540Sbill 	outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8);
38245540Sbill 	wecmd.cs_txp = 1;
38345540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
38441003Swilliam 	(void) splx(s);
38541003Swilliam }
38641003Swilliam 
38741003Swilliam /*
38841003Swilliam  * Ethernet interface interrupt processor
38941003Swilliam  */
39045540Sbill weintr(unit)
39141003Swilliam 	int unit;
39241003Swilliam {
393*49596Swilliam 	register struct we_softc *sc = &we_softc[unit];
39445540Sbill 	union we_command wecmd;
39545540Sbill 	union we_interrupt weisr;
396*49596Swilliam 
39745540Sbill 	unit =0;
39841003Swilliam 
39941003Swilliam 	/* disable onboard interrupts, then get interrupt status */
40045540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
40145540Sbill 	wecmd.cs_ps = 0;
40245540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
403*49596Swilliam 	/*outb(sc->we_io_nic_addr + WD_P0_IMR, 0);*/
40445540Sbill 	weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
405*49596Swilliam loop:
406*49596Swilliam 	outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte);
40741003Swilliam 
40841003Swilliam 	/* transmit error */
40945540Sbill 	if (weisr.is_txe) {
41041003Swilliam 		/* need to read these registers to clear status */
41145540Sbill 		sc->we_if.if_collisions +=
41245540Sbill 		    inb(sc->we_io_nic_addr + WD_P0_TBCR0);
41345540Sbill 		++sc->we_if.if_oerrors;
41441003Swilliam 	}
41541003Swilliam 
41641003Swilliam 	/* receiver error */
41745540Sbill 	if (weisr.is_rxe) {
41841003Swilliam 		/* need to read these registers to clear status */
41945540Sbill 		(void) inb(sc->we_io_nic_addr + 0xD);
42045540Sbill 		(void) inb(sc->we_io_nic_addr + 0xE);
42145540Sbill 		(void) inb(sc->we_io_nic_addr + 0xF);
42245540Sbill 		++sc->we_if.if_ierrors;
42341003Swilliam 	}
42441003Swilliam 
42541003Swilliam 	/* normal transmit complete */
426*49596Swilliam 	if (weisr.is_ptx || weisr.is_txe)
42745540Sbill 		wetint (unit);
42841003Swilliam 
42941003Swilliam 	/* normal receive notification */
430*49596Swilliam 	if (weisr.is_prx || weisr.is_rxe)
43145540Sbill 		werint (unit);
43241003Swilliam 
43341003Swilliam 	/* try to start transmit */
434*49596Swilliam 	westart(&sc->we_if);
43541003Swilliam 
43641003Swilliam 	/* re-enable onboard interrupts */
43745540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
43845540Sbill 	wecmd.cs_ps = 0;
43945540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
440*49596Swilliam 	outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/);
441*49596Swilliam 	weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
442*49596Swilliam 	if (weisr.is_byte) goto loop;
44341003Swilliam }
44441003Swilliam 
44541003Swilliam /*
44641003Swilliam  * Ethernet interface transmit interrupt.
44741003Swilliam  */
44845540Sbill wetint(unit)
44941003Swilliam 	int unit;
45041003Swilliam {
45145540Sbill 	register struct we_softc *sc = &we_softc[unit];
45241003Swilliam 
45341003Swilliam 	/*
45441003Swilliam 	 * Do some statistics (assume page zero of NIC mapped in)
45541003Swilliam 	 */
45645540Sbill 	sc->we_flags &= ~WDF_TXBUSY;
45745540Sbill 	sc->we_if.if_timer = 0;
45845540Sbill 	++sc->we_if.if_opackets;
45945540Sbill 	sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0);
46041003Swilliam }
46141003Swilliam 
46241003Swilliam /*
46341003Swilliam  * Ethernet interface receiver interrupt.
46441003Swilliam  */
46545540Sbill werint(unit)
46641003Swilliam 	int unit;
46741003Swilliam {
46845540Sbill 	register struct we_softc *sc = &we_softc[unit];
46941003Swilliam 	u_char bnry, curr;
470*49596Swilliam 	long len;
47145540Sbill 	union we_command wecmd;
47245540Sbill 	struct we_ring *wer;
47341003Swilliam 
47441003Swilliam 	/*
47541003Swilliam 	 * Traverse the receive ring looking for packets to pass back.
47641003Swilliam 	 * The search is complete when we find a descriptor not in use.
47741003Swilliam 	 */
47845540Sbill 	wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
47945540Sbill 	wecmd.cs_ps = 0;
48045540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
48145540Sbill 	bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY);
48245540Sbill 	wecmd.cs_ps = 1;
48345540Sbill 	outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
48445540Sbill 	curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
485*49596Swilliam if(Bdry)
48645540Sbill 	bnry =Bdry;
487*49596Swilliam 
488*49596Swilliam /*printf("B %d c %d ", bnry, curr);*/
48941003Swilliam 	while (bnry != curr)
49041003Swilliam 	{
49141003Swilliam 		/* get pointer to this buffer header structure */
49245540Sbill 		wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8));
49341003Swilliam 
494*49596Swilliam 		/* count includes CRC */
495*49596Swilliam 		len = wer->we_count - 4;
496*49596Swilliam 		if (len > 30 && len <= ETHERMTU+100
497*49596Swilliam 			/*&& (*(char *)wer  == 1 || *(char *) wer == 0x21)*/)
498*49596Swilliam 			weread(sc, (caddr_t)(wer + 1), len);
499*49596Swilliam 		else printf("reject %d", len);
50041003Swilliam 
50141003Swilliam outofbufs:
50245540Sbill 		wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
50345540Sbill 		wecmd.cs_ps = 0;
50445540Sbill 		outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
50541003Swilliam 
506*49596Swilliam 		/* advance on chip Boundry register */
507*49596Swilliam 		if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) {
508*49596Swilliam 			bnry = WD_TXBUF_SIZE;
509*49596Swilliam 			outb(sc->we_io_nic_addr + WD_P0_BNRY,
510*49596Swilliam 					sc->we_vmem_size / WD_PAGE_SIZE-1);
511*49596Swilliam 
512*49596Swilliam 		} else {
513*49596Swilliam 			if (len > 30 && len <= ETHERMTU+100)
514*49596Swilliam 				bnry = wer->we_next_packet;
515*49596Swilliam 			else bnry = curr;
516*49596Swilliam 
517*49596Swilliam 			/* watch out for NIC overflow, reset Boundry if invalid */
518*49596Swilliam 			if ((bnry - 1) < WD_TXBUF_SIZE) {
519*49596Swilliam 		    		outb(sc->we_io_nic_addr + WD_P0_BNRY,
520*49596Swilliam 					(sc->we_vmem_size / WD_PAGE_SIZE) - 1);
521*49596Swilliam 				bnry = WD_TXBUF_SIZE;
522*49596Swilliam 			} else
523*49596Swilliam 				outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1);
52441003Swilliam 		}
52541003Swilliam 
52641003Swilliam 		/* refresh our copy of CURR */
52745540Sbill 		wecmd.cs_ps = 1;
52845540Sbill 		outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
52945540Sbill 		curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
530*49596Swilliam /*printf("b %d c %d ", bnry, curr); */
53141003Swilliam 	}
53245540Sbill Bdry = bnry;
53341003Swilliam }
53441003Swilliam 
535*49596Swilliam #ifdef shit
53641003Swilliam /*
537*49596Swilliam  * Process an ioctl request.
53841003Swilliam  */
539*49596Swilliam weioctl(ifp, cmd, data)
540*49596Swilliam 	register struct ifnet *ifp;
541*49596Swilliam 	int cmd;
542*49596Swilliam 	caddr_t data;
54341003Swilliam {
544*49596Swilliam 	struct we_softc *sc = &we_softc[ifp->if_unit];
545*49596Swilliam 	struct ifaddr *ifa = (struct ifaddr *)data;
546*49596Swilliam 	int s = splimp(), error = 0;
54741003Swilliam 
548*49596Swilliam 	switch (cmd) {
54941003Swilliam 
550*49596Swilliam 	case SIOCSIFADDR:
551*49596Swilliam 		ifp->if_flags |= IFF_UP;
552*49596Swilliam 		weinit(ifp->if_unit);
553*49596Swilliam 		switch(ifa->ifa_addr->sa_family) {
55441003Swilliam #ifdef INET
555*49596Swilliam 		case AF_INET:
556*49596Swilliam 			((struct arpcom *)ifp)->ac_ipaddr =
557*49596Swilliam 				IA_SIN(ifa)->sin_addr;
558*49596Swilliam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
559*49596Swilliam 			break;
56041003Swilliam #endif
56141003Swilliam #ifdef NS
562*49596Swilliam 		case AF_NS:
563*49596Swilliam 		    {
564*49596Swilliam 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
565*49596Swilliam 
566*49596Swilliam 			if (ns_nullhost(*ina))
567*49596Swilliam 				ina->x_host = *(union ns_host *)(sc->we_addr);
568*49596Swilliam 			else
569*49596Swilliam 				wesetaddr(ina->x_host.c_host, ifp->if_unit);
570*49596Swilliam 			break;
571*49596Swilliam 		    }
572*49596Swilliam #endif
573*49596Swilliam 		}
57441003Swilliam 		break;
57541003Swilliam 
576*49596Swilliam 	case SIOCSIFFLAGS:
577*49596Swilliam 		if (((ifp->if_flags & IFF_UP) == 0) &&
578*49596Swilliam 		   (sc->we_flags & WDF_RUNNING)) {
579*49596Swilliam 			westop(ifp->if_unit);
580*49596Swilliam 		} else if (((ifp->if_flags & IFF_UP) == IFF_UP) &&
581*49596Swilliam 		   ((sc->we_flags & WDF_RUNNING) == 0))
582*49596Swilliam 			weinit(ifp->if_unit);
58341003Swilliam 		break;
584*49596Swilliam 
58541003Swilliam 	default:
586*49596Swilliam 		error = EINVAL;
58741003Swilliam 
58841003Swilliam 	}
58941003Swilliam 	(void) splx(s);
59041003Swilliam 	return (error);
59141003Swilliam }
592*49596Swilliam #endif
59341003Swilliam 
59441003Swilliam /*
59541003Swilliam  * Process an ioctl request.
59641003Swilliam  */
59745540Sbill weioctl(ifp, cmd, data)
59841003Swilliam 	register struct ifnet *ifp;
59941003Swilliam 	int cmd;
60041003Swilliam 	caddr_t data;
60141003Swilliam {
602*49596Swilliam 	register struct ifaddr *ifa = (struct ifaddr *)data;
60345540Sbill 	struct we_softc *sc = &we_softc[ifp->if_unit];
604*49596Swilliam 	struct ifreq *ifr = (struct ifreq *)data;
60541003Swilliam 	int s = splimp(), error = 0;
606*49596Swilliam 
607*49596Swilliam 
60841003Swilliam 	switch (cmd) {
609*49596Swilliam 
61041003Swilliam 	case SIOCSIFADDR:
61141003Swilliam 		ifp->if_flags |= IFF_UP;
612*49596Swilliam 
613*49596Swilliam 		switch (ifa->ifa_addr->sa_family) {
61441003Swilliam #ifdef INET
61541003Swilliam 		case AF_INET:
616*49596Swilliam 			weinit(ifp->if_unit);	/* before arpwhohas */
61741003Swilliam 			((struct arpcom *)ifp)->ac_ipaddr =
61841003Swilliam 				IA_SIN(ifa)->sin_addr;
61941003Swilliam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
62041003Swilliam 			break;
62141003Swilliam #endif
62241003Swilliam #ifdef NS
62341003Swilliam 		case AF_NS:
62441003Swilliam 		    {
62541003Swilliam 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
626*49596Swilliam 
62741003Swilliam 			if (ns_nullhost(*ina))
628*49596Swilliam 				ina->x_host = *(union ns_host *)(sc->ns_addr);
629*49596Swilliam 			else {
630*49596Swilliam 				/*
631*49596Swilliam 				 * The manual says we can't change the address
632*49596Swilliam 				 * while the receiver is armed,
633*49596Swilliam 				 * so reset everything
634*49596Swilliam 				 */
635*49596Swilliam 				ifp->if_flags &= ~IFF_RUNNING;
636*49596Swilliam 				bcopy((caddr_t)ina->x_host.c_host,
637*49596Swilliam 				    (caddr_t)sc->ns_addr, sizeof(sc->ns_addr));
638*49596Swilliam 			}
639*49596Swilliam 			weinit(ifp->if_unit); /* does ne_setaddr() */
64041003Swilliam 			break;
64141003Swilliam 		    }
64241003Swilliam #endif
643*49596Swilliam 		default:
644*49596Swilliam 			weinit(ifp->if_unit);
645*49596Swilliam 			break;
64641003Swilliam 		}
64741003Swilliam 		break;
64841003Swilliam 
64941003Swilliam 	case SIOCSIFFLAGS:
650*49596Swilliam 		if ((ifp->if_flags & IFF_UP) == 0 &&
651*49596Swilliam 		    ifp->if_flags & IFF_RUNNING) {
652*49596Swilliam 			ifp->if_flags &= ~IFF_RUNNING;
65345540Sbill 			westop(ifp->if_unit);
654*49596Swilliam 		} else if (ifp->if_flags & IFF_UP &&
655*49596Swilliam 		    (ifp->if_flags & IFF_RUNNING) == 0)
65645540Sbill 			weinit(ifp->if_unit);
65741003Swilliam 		break;
65841003Swilliam 
659*49596Swilliam #ifdef notdef
660*49596Swilliam 	case SIOCGHWADDR:
661*49596Swilliam 		bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data,
662*49596Swilliam 			sizeof(sc->sc_addr));
663*49596Swilliam 		break;
664*49596Swilliam #endif
665*49596Swilliam 
66641003Swilliam 	default:
66741003Swilliam 		error = EINVAL;
66841003Swilliam 	}
669*49596Swilliam 	splx(s);
67041003Swilliam 	return (error);
67141003Swilliam }
67241003Swilliam /*
67341003Swilliam  * set ethernet address for unit
67441003Swilliam  */
67545540Sbill wesetaddr(physaddr, unit)
67641003Swilliam 	u_char *physaddr;
67741003Swilliam 	int unit;
67841003Swilliam {
67945540Sbill 	register struct we_softc *sc = &we_softc[unit];
68041003Swilliam 	register int i;
68141003Swilliam 
68241003Swilliam 	/*
68341003Swilliam 	 * Rewrite ethernet address, and then force restart of NIC
68441003Swilliam 	 */
68541003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; i++)
68645540Sbill 		sc->we_addr[i] = physaddr[i];
68745540Sbill 	sc->we_flags &= ~WDF_RUNNING;
68845540Sbill 	weinit(unit);
68941003Swilliam }
69041003Swilliam 
691*49596Swilliam #define	wedataaddr(sc, eh, off, type) \
692*49596Swilliam 	((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \
693*49596Swilliam 		(((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \
694*49596Swilliam 		+ (sc)->we_vmem_ring: \
695*49596Swilliam 		((caddr_t)((eh)+1)+(off)))
69641003Swilliam /*
69741003Swilliam  * Pass a packet to the higher levels.
698*49596Swilliam  * We deal with the trailer protocol here.
69941003Swilliam  */
700*49596Swilliam weread(sc, buf, len)
70145540Sbill 	register struct we_softc *sc;
702*49596Swilliam 	char *buf;
703*49596Swilliam 	int len;
70441003Swilliam {
705*49596Swilliam 	register struct ether_header *eh;
706*49596Swilliam     	struct mbuf *m, *weget();
707*49596Swilliam 	int off, resid;
708*49596Swilliam 
70941003Swilliam 	/*
710*49596Swilliam 	 * Deal with trailer protocol: if type is trailer type
711*49596Swilliam 	 * get true type from first 16-bit word past data.
712*49596Swilliam 	 * Remember that type was trailer by setting off.
71341003Swilliam 	 */
714*49596Swilliam 	eh = (struct ether_header *)buf;
715*49596Swilliam 	eh->ether_type = ntohs((u_short)eh->ether_type);
716*49596Swilliam 	if (eh->ether_type >= ETHERTYPE_TRAIL &&
717*49596Swilliam 	    eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
718*49596Swilliam 		off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
719*49596Swilliam 		if (off >= ETHERMTU) return;		/* sanity */
720*49596Swilliam 		eh->ether_type = ntohs(*wedataaddr(sc, eh, off, u_short *));
721*49596Swilliam 		resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *)));
722*49596Swilliam 		if (off + resid > len) return;		/* sanity */
723*49596Swilliam 		len = off + resid;
724*49596Swilliam 	} else	off = 0;
72541003Swilliam 
726*49596Swilliam 	len -= sizeof(struct ether_header);
727*49596Swilliam 	if (len <= 0) return;
728*49596Swilliam 
72941003Swilliam 	/*
730*49596Swilliam 	 * Pull packet off interface.  Off is nonzero if packet
731*49596Swilliam 	 * has trailing header; neget will then force this header
732*49596Swilliam 	 * information to be at the front, but we still have to drop
733*49596Swilliam 	 * the type and length which are at the front of any trailer data.
73441003Swilliam 	 */
735*49596Swilliam 	m = weget(buf, len, off, &sc->we_if, sc);
736*49596Swilliam 	if (m == 0) return;
737*49596Swilliam 	ether_input(&sc->we_if, eh, m);
738*49596Swilliam }
73941003Swilliam 
740*49596Swilliam /*
741*49596Swilliam  * Supporting routines
742*49596Swilliam  */
74341003Swilliam 
744*49596Swilliam /*
745*49596Swilliam  * Pull read data off a interface.
746*49596Swilliam  * Len is length of data, with local net header stripped.
747*49596Swilliam  * Off is non-zero if a trailer protocol was used, and
748*49596Swilliam  * gives the offset of the trailer information.
749*49596Swilliam  * We copy the trailer information and then all the normal
750*49596Swilliam  * data into mbufs.  When full cluster sized units are present
751*49596Swilliam  * we copy into clusters.
752*49596Swilliam  */
753*49596Swilliam struct mbuf *
754*49596Swilliam weget(buf, totlen, off0, ifp, sc)
755*49596Swilliam 	caddr_t buf;
756*49596Swilliam 	int totlen, off0;
757*49596Swilliam 	struct ifnet *ifp;
758*49596Swilliam 	struct we_softc *sc;
759*49596Swilliam {
760*49596Swilliam 	struct mbuf *top, **mp, *m, *p;
761*49596Swilliam 	int off = off0, len;
762*49596Swilliam 	register caddr_t cp = buf;
763*49596Swilliam 	char *epkt;
764*49596Swilliam int tc =totlen;
76541003Swilliam 
766*49596Swilliam /*
767*49596Swilliam printf("\nR");
768*49596Swilliam { int j;
769*49596Swilliam for(j=0; j < sizeof(struct ether_header);j++) puthex(buf[j]);
770*49596Swilliam printf("|");
771*49596Swilliam }*/
772*49596Swilliam 	buf += sizeof(struct ether_header);
773*49596Swilliam 	cp = buf;
774*49596Swilliam 	epkt = cp + totlen;
775*49596Swilliam 
776*49596Swilliam 	if (off) {
777*49596Swilliam 		cp += off + 2 * sizeof(u_short);
778*49596Swilliam 		totlen -= 2 * sizeof(u_short);
779*49596Swilliam 	}
780*49596Swilliam 
781*49596Swilliam 	MGETHDR(m, M_DONTWAIT, MT_DATA);
782*49596Swilliam 	if (m == 0)
783*49596Swilliam 		return (0);
784*49596Swilliam 	m->m_pkthdr.rcvif = ifp;
785*49596Swilliam 	m->m_pkthdr.len = totlen;
786*49596Swilliam 	m->m_len = MHLEN;
787*49596Swilliam 
788*49596Swilliam 	top = 0;
789*49596Swilliam 	mp = &top;
790*49596Swilliam 	while (totlen > 0) {
791*49596Swilliam 		if (top) {
792*49596Swilliam 			MGET(m, M_DONTWAIT, MT_DATA);
793*49596Swilliam 			if (m == 0) {
794*49596Swilliam 				m_freem(top);
795*49596Swilliam 				return (0);
796*49596Swilliam 			}
797*49596Swilliam 			m->m_len = MLEN;
798*49596Swilliam 		}
799*49596Swilliam 		len = min(totlen, epkt - cp);
800*49596Swilliam #ifdef nope
801*49596Swilliam 		/* only do up to end of buffer */
802*49596Swilliam 		if (epkt > sc->we_vmem_end)
803*49596Swilliam 			len = min(len, sc->we_vmem_end - cp);
80441003Swilliam #endif
805*49596Swilliam 		if (len >= MINCLSIZE) {
806*49596Swilliam 			MCLGET(m, M_DONTWAIT);
807*49596Swilliam 			if (m->m_flags & M_EXT)
808*49596Swilliam 				m->m_len = len = min(len, MCLBYTES);
809*49596Swilliam 			else
810*49596Swilliam 				len = m->m_len;
811*49596Swilliam 		} else {
812*49596Swilliam 			/*
813*49596Swilliam 			 * Place initial small packet/header at end of mbuf.
814*49596Swilliam 			 */
815*49596Swilliam 			if (len < m->m_len) {
816*49596Swilliam 				if (top == 0 && len + max_linkhdr <= m->m_len)
817*49596Swilliam 					m->m_data += max_linkhdr;
818*49596Swilliam 				m->m_len = len;
819*49596Swilliam 			} else
820*49596Swilliam 				len = m->m_len;
821*49596Swilliam 		}
82241003Swilliam 
823*49596Swilliam 		totlen -= len;
824*49596Swilliam 		/* only do up to end of buffer */
825*49596Swilliam 		if (cp+len > sc->we_vmem_end) {
826*49596Swilliam 			unsigned toend = sc->we_vmem_end - cp;
827*49596Swilliam 
828*49596Swilliam 			bcopy(cp, mtod(m, caddr_t), toend);
829*49596Swilliam 			cp = sc->we_vmem_ring;
830*49596Swilliam 			bcopy(cp, mtod(m, caddr_t)+toend, len - toend);
831*49596Swilliam 			cp += len - toend;
832*49596Swilliam 			epkt = cp + totlen;
833*49596Swilliam 		} else {
834*49596Swilliam 			bcopy(cp, mtod(m, caddr_t), (unsigned)len);
835*49596Swilliam 			cp += len;
836*49596Swilliam 		}
837*49596Swilliam /*{ int j;
838*49596Swilliam for(j=0; j < m->m_len;j++) puthex(mtod(m, char *)[j]);
839*49596Swilliam printf("|");
840*49596Swilliam }*/
841*49596Swilliam 		*mp = m;
842*49596Swilliam 		mp = &m->m_next;
843*49596Swilliam 		if (cp == epkt) {
844*49596Swilliam 			cp = buf;
845*49596Swilliam 			epkt = cp + tc;
846*49596Swilliam 		}
84741003Swilliam 	}
848*49596Swilliam /*printf("%d ",tc); */
849*49596Swilliam 	return (top);
85041003Swilliam }
85141003Swilliam 
852*49596Swilliam puthex(c){
853*49596Swilliam 	printf("%x",(c>>4)&0xf);
854*49596Swilliam 	printf("%x",c&0xf);
855*49596Swilliam }
85641003Swilliam #endif
857