xref: /csrg-svn/sys/i386/isa/if_we.c (revision 41003)
1*41003Swilliam /*-
2*41003Swilliam  * Copyright (c) 1990 The Regents of the University of California.
3*41003Swilliam  * All rights reserved.
4*41003Swilliam  *
5*41003Swilliam  * This code is derived from software contributed to Berkeley by
6*41003Swilliam  * Tim L. Tucker
7*41003Swilliam  *
8*41003Swilliam  * %sccs.include.noredist.c%
9*41003Swilliam  *
10*41003Swilliam  *	@(#)if_we.c	5.1 (Berkeley) 04/22/90
11*41003Swilliam  */
12*41003Swilliam 
13*41003Swilliam /*
14*41003Swilliam  * Modification history
15*41003Swilliam  *
16*41003Swilliam  * 8/28/89 - Initial version, Tim L Tucker
17*41003Swilliam  */
18*41003Swilliam 
19*41003Swilliam #include "wd.h"
20*41003Swilliam #if	NWD > 0
21*41003Swilliam /*
22*41003Swilliam  * Western Digital 8003 ethernet/starlan adapter
23*41003Swilliam  *
24*41003Swilliam  * Supports the following interface cards:
25*41003Swilliam  * WD8003E, WD8003EBT, WD8003S, WD8003SBT
26*41003Swilliam  *
27*41003Swilliam  * The Western Digital card is one of many AT/MCA ethernet interfaces
28*41003Swilliam  * based on the National N8390/NS32490 Network Interface chip set.
29*41003Swilliam  */
30*41003Swilliam #include "param.h"
31*41003Swilliam #include "mbuf.h"
32*41003Swilliam #include "socket.h"
33*41003Swilliam #include "ioctl.h"
34*41003Swilliam #include "errno.h"
35*41003Swilliam #include "syslog.h"
36*41003Swilliam 
37*41003Swilliam #include "../net/if.h"
38*41003Swilliam #include "../net/netisr.h"
39*41003Swilliam 
40*41003Swilliam #ifdef INET
41*41003Swilliam #include "../netinet/in.h"
42*41003Swilliam #include "../netinet/in_systm.h"
43*41003Swilliam #include "../netinet/in_var.h"
44*41003Swilliam #include "../netinet/ip.h"
45*41003Swilliam #include "../netinet/if_ether.h"
46*41003Swilliam #endif
47*41003Swilliam 
48*41003Swilliam #ifdef NS
49*41003Swilliam #include "../netns/ns.h"
50*41003Swilliam #include "../netns/ns_if.h"
51*41003Swilliam #endif
52*41003Swilliam 
53*41003Swilliam #include "if_wdreg.h"
54*41003Swilliam #include "../isa/isavar.h"
55*41003Swilliam 
56*41003Swilliam /*
57*41003Swilliam  * This constant should really be 60 because the wd adds 4 bytes of crc.
58*41003Swilliam  * However when set to 60 our packets are ignored by deuna's , 3coms are
59*41003Swilliam  * okay ??????????????????????????????????????????
60*41003Swilliam  */
61*41003Swilliam #define ETHER_MIN_LEN 64
62*41003Swilliam #define	ETHER_ADDR_LEN 6
63*41003Swilliam #define ETHER_HDR_SIZE 14
64*41003Swilliam 
65*41003Swilliam /*
66*41003Swilliam  * Ethernet software status per interface.
67*41003Swilliam  *
68*41003Swilliam  * Each interface is referenced by a network interface structure,
69*41003Swilliam  * qe_if, which the routing code uses to locate the interface.
70*41003Swilliam  * This structure contains the output queue for the interface, its address, ...
71*41003Swilliam  */
72*41003Swilliam struct	wd_softc {
73*41003Swilliam 	struct	arpcom wd_ac;		/* Ethernet common part 	*/
74*41003Swilliam #define	wd_if	wd_ac.ac_if		/* network-visible interface 	*/
75*41003Swilliam #define	wd_addr	wd_ac.ac_enaddr		/* hardware Ethernet address 	*/
76*41003Swilliam 
77*41003Swilliam 	u_char	wd_flags;		/* software state		*/
78*41003Swilliam #define	WDF_RUNNING	0x01
79*41003Swilliam #define WDF_TXBUSY	0x02
80*41003Swilliam 
81*41003Swilliam 	u_char	wd_type;		/* interface type code		*/
82*41003Swilliam 	u_short	wd_vector;		/* interrupt vector 		*/
83*41003Swilliam 	caddr_t	wd_io_ctl_addr;		/* i/o bus address, control	*/
84*41003Swilliam 	caddr_t	wd_io_nic_addr;		/* i/o bus address, NS32490	*/
85*41003Swilliam 
86*41003Swilliam 	caddr_t	wd_vmem_addr;		/* card RAM virtual memory base */
87*41003Swilliam 	u_long	wd_vmem_size;		/* card RAM bytes		*/
88*41003Swilliam 	caddr_t	wd_vmem_ring;		/* receive ring RAM vaddress	*/
89*41003Swilliam } wd_softc[NWD];
90*41003Swilliam 
91*41003Swilliam int	wdprobe(), wdattach(), wdintr();
92*41003Swilliam int	wdinit(), wdoutput(), wdioctl(), wdreset();
93*41003Swilliam 
94*41003Swilliam /*
95*41003Swilliam  * Probe the WD8003 to see if it's there
96*41003Swilliam  */
97*41003Swilliam wdprobe(reg, is)
98*41003Swilliam 	caddr_t reg;
99*41003Swilliam 	struct isa_device *is;
100*41003Swilliam {
101*41003Swilliam 	register int i;
102*41003Swilliam 	register struct wd_softc *sc = &wd_softc[is->is_unit];
103*41003Swilliam 	union wd_mem_sel wdm;
104*41003Swilliam 	u_char sum;
105*41003Swilliam 
106*41003Swilliam 	/*
107*41003Swilliam 	 * Here we check the card ROM, if the checksum passes, and the
108*41003Swilliam 	 * type code and ethernet address check out, then we know we have
109*41003Swilliam 	 * a wd8003 card.
110*41003Swilliam 	 *
111*41003Swilliam 	 * Autoconfiguration: No warning message is printed on error.
112*41003Swilliam 	 */
113*41003Swilliam 	for (sum = 0, i = 0; i < 8; ++i)
114*41003Swilliam 	    sum += INB(reg + WD_ROM_OFFSET + i);
115*41003Swilliam 	if (sum != WD_CHECKSUM)
116*41003Swilliam             return (0);
117*41003Swilliam 	sc->wd_type = INB(reg + WD_ROM_OFFSET + 6);
118*41003Swilliam 	if ((sc->wd_type != WD_ETHER) && (sc->wd_type != WD_STARLAN))
119*41003Swilliam             return (0);
120*41003Swilliam 
121*41003Swilliam 	/*
122*41003Swilliam 	 * Setup card RAM area and i/o addresses
123*41003Swilliam 	 * Kernel Virtual to segment C0000-DFFFF?????
124*41003Swilliam 	 */
125*41003Swilliam 	sc->wd_io_ctl_addr = reg;
126*41003Swilliam 	sc->wd_io_nic_addr = sc->wd_io_ctl_addr + WD_NIC_OFFSET;
127*41003Swilliam 	sc->wd_vector = is->is_vector;
128*41003Swilliam 	sc->wd_vmem_addr = (caddr_t)is->is_mem;
129*41003Swilliam 	sc->wd_vmem_size = is->is_memsize;
130*41003Swilliam 	sc->wd_vmem_ring = sc->wd_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE);
131*41003Swilliam 
132*41003Swilliam 	/*
133*41003Swilliam 	 * Save board ROM station address
134*41003Swilliam 	 */
135*41003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; ++i)
136*41003Swilliam 	    sc->wd_addr[i] = INB(sc->wd_io_ctl_addr + WD_ROM_OFFSET + i);
137*41003Swilliam 
138*41003Swilliam 	/*
139*41003Swilliam 	 * Mapin interface memory, setup memory select register
140*41003Swilliam 	 */
141*41003Swilliam 	wdm.ms_addr = (u_long)sc->wd_vmem_addr >> 13;
142*41003Swilliam 	wdm.ms_enable = 1;
143*41003Swilliam 	wdm.ms_reset = 0;
144*41003Swilliam 	OUTB(sc->wd_io_ctl_addr, wdm.ms_byte);
145*41003Swilliam 
146*41003Swilliam 	/*
147*41003Swilliam 	 * clear interface memory, then sum to make sure its valid
148*41003Swilliam 	 */
149*41003Swilliam 	for (i = 0; i < sc->wd_vmem_size; ++i)
150*41003Swilliam 	    sc->wd_vmem_addr[i] = 0x0;
151*41003Swilliam 	for (sum = 0, i = 0; i < sc->wd_vmem_size; ++i)
152*41003Swilliam 	    sum += sc->wd_vmem_addr[i];
153*41003Swilliam 	if (sum != 0x0) {
154*41003Swilliam             printf("wd%d: wd8003 dual port RAM address error\n", is->is_unit);
155*41003Swilliam 	    return (0);
156*41003Swilliam 	}
157*41003Swilliam 
158*41003Swilliam 	return (WD_IO_PORTS);
159*41003Swilliam }
160*41003Swilliam 
161*41003Swilliam /*
162*41003Swilliam  * Interface exists: make available by filling in network interface
163*41003Swilliam  * record.  System will initialize the interface when it is ready
164*41003Swilliam  * to accept packets.
165*41003Swilliam  */
166*41003Swilliam wdattach(is)
167*41003Swilliam 	struct isa_device *is;
168*41003Swilliam {
169*41003Swilliam 	register struct wd_softc *sc = &wd_softc[is->is_unit];
170*41003Swilliam 	register struct ifnet *ifp = &sc->wd_if;
171*41003Swilliam 
172*41003Swilliam 	/*
173*41003Swilliam 	 * Initialize ifnet structure
174*41003Swilliam 	 */
175*41003Swilliam 	ifp->if_unit = is->is_unit;
176*41003Swilliam 	ifp->if_name = "wd";
177*41003Swilliam 	ifp->if_mtu = ETHERMTU;
178*41003Swilliam 	ifp->if_flags = IFF_BROADCAST|IFF_NOTRAILERS;
179*41003Swilliam 	ifp->if_init = wdinit;
180*41003Swilliam 	ifp->if_output = wdoutput;
181*41003Swilliam 	ifp->if_ioctl = wdioctl;
182*41003Swilliam 	ifp->if_reset = wdreset;
183*41003Swilliam 	ifp->if_watchdog = 0;
184*41003Swilliam 	if_attach(ifp);
185*41003Swilliam 
186*41003Swilliam 	/*
187*41003Swilliam 	 * Banner...
188*41003Swilliam 	 */
189*41003Swilliam 	printf("wd%d: %s, hardware address %s\n", is->is_unit,
190*41003Swilliam 		((sc->wd_type == WD_ETHER) ? "ethernet" : "starlan"),
191*41003Swilliam 		ether_sprintf(sc->wd_addr));
192*41003Swilliam }
193*41003Swilliam 
194*41003Swilliam /*
195*41003Swilliam  * Reset of interface.
196*41003Swilliam  */
197*41003Swilliam wdreset(unit, uban)
198*41003Swilliam 	int unit, uban;
199*41003Swilliam {
200*41003Swilliam 	if (unit >= NWD)
201*41003Swilliam 		return;
202*41003Swilliam 	printf("wd%d: reset\n", unit);
203*41003Swilliam 	wd_softc[unit].wd_flags &= ~WDF_RUNNING;
204*41003Swilliam 	wdinit(unit);
205*41003Swilliam }
206*41003Swilliam 
207*41003Swilliam /*
208*41003Swilliam  * Take interface offline.
209*41003Swilliam  */
210*41003Swilliam wdstop(unit)
211*41003Swilliam 	int unit;
212*41003Swilliam {
213*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
214*41003Swilliam 	union wd_command wdcmd;
215*41003Swilliam 	int s;
216*41003Swilliam 
217*41003Swilliam 	/*
218*41003Swilliam 	 * Shutdown NS32490
219*41003Swilliam 	 */
220*41003Swilliam 	s = splimp();
221*41003Swilliam 	wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
222*41003Swilliam 	wdcmd.cs_stp = 1;
223*41003Swilliam 	wdcmd.cs_sta = 0;
224*41003Swilliam 	wdcmd.cs_ps = 0;
225*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
226*41003Swilliam 	(void) splx(s);
227*41003Swilliam }
228*41003Swilliam 
229*41003Swilliam /*
230*41003Swilliam  * Initialization of interface (really just NS32490).
231*41003Swilliam  */
232*41003Swilliam wdinit(unit)
233*41003Swilliam 	int unit;
234*41003Swilliam {
235*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
236*41003Swilliam 	register struct ifnet *ifp = &sc->wd_if;
237*41003Swilliam 	union wd_command wdcmd;
238*41003Swilliam 	int i, s;
239*41003Swilliam 
240*41003Swilliam 	/* address not known */
241*41003Swilliam 	if (ifp->if_addrlist == (struct ifaddr *)0)
242*41003Swilliam 		return;
243*41003Swilliam 
244*41003Swilliam 	/* already running */
245*41003Swilliam 	if (sc->wd_flags & WDF_RUNNING)
246*41003Swilliam 		return;
247*41003Swilliam 
248*41003Swilliam 	/*
249*41003Swilliam 	 * Initialize NS32490 in order given in NSC NIC manual.
250*41003Swilliam 	 * this is stock code...please see the National manual for details.
251*41003Swilliam 	 */
252*41003Swilliam 	s = splhi();
253*41003Swilliam 	wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
254*41003Swilliam 	wdcmd.cs_stp = 1;
255*41003Swilliam 	wdcmd.cs_sta = 0;
256*41003Swilliam 	wdcmd.cs_ps = 0;
257*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
258*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_DCR, WD_D_CONFIG);
259*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_RBCR0, 0);
260*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_RBCR1, 0);
261*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_RCR, WD_R_MON);
262*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_TCR, WD_T_CONFIG);
263*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_TPSR, 0);
264*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE);
265*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_PSTOP,
266*41003Swilliam 		sc->wd_vmem_size / WD_PAGE_SIZE);
267*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE);
268*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_ISR, 0xff);
269*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
270*41003Swilliam 	wdcmd.cs_ps = 1;
271*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
272*41003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; ++i)
273*41003Swilliam 	    OUTB(sc->wd_io_nic_addr + WD_P1_PAR0 + i, sc->wd_addr[i]);
274*41003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; ++i)	/* == broadcast addr */
275*41003Swilliam 	    OUTB(sc->wd_io_nic_addr + WD_P1_MAR0 + i, 0xff);
276*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE);
277*41003Swilliam 	wdcmd.cs_ps = 0;
278*41003Swilliam 	wdcmd.cs_stp = 0;
279*41003Swilliam 	wdcmd.cs_sta = 1;
280*41003Swilliam 	wdcmd.cs_rd = 0x4;
281*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P1_COMMAND, wdcmd.cs_byte);
282*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_RCR, WD_R_CONFIG);
283*41003Swilliam 
284*41003Swilliam 	/*
285*41003Swilliam 	 * Take the interface out of reset, program the vector,
286*41003Swilliam 	 * enable interrupts, and tell the world we are up.
287*41003Swilliam 	 */
288*41003Swilliam 	ifp->if_flags |= IFF_UP | IFF_RUNNING;
289*41003Swilliam 	sc->wd_flags |= WDF_RUNNING;
290*41003Swilliam 	sc->wd_flags &= ~WDF_TXBUSY;
291*41003Swilliam 	(void) splx(s);
292*41003Swilliam 	wdstart(unit);
293*41003Swilliam }
294*41003Swilliam 
295*41003Swilliam /*
296*41003Swilliam  * Start output on interface.
297*41003Swilliam  */
298*41003Swilliam wdstart(unit)
299*41003Swilliam 	int unit;
300*41003Swilliam {
301*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
302*41003Swilliam 	struct mbuf *m0, *m;
303*41003Swilliam 	register caddr_t buffer;
304*41003Swilliam 	int len = 0, s;
305*41003Swilliam 	union wd_command wdcmd;
306*41003Swilliam 
307*41003Swilliam 	/*
308*41003Swilliam 	 * The NS32490 has only one transmit buffer, if it is busy we
309*41003Swilliam 	 * must wait until the transmit interrupt completes.
310*41003Swilliam 	 */
311*41003Swilliam 	s = splhi();
312*41003Swilliam 	if (sc->wd_flags & WDF_TXBUSY) {
313*41003Swilliam 		(void) splx(s);
314*41003Swilliam 		return;
315*41003Swilliam 	}
316*41003Swilliam 	IF_DEQUEUE(&sc->wd_if.if_snd, m);
317*41003Swilliam 	if (m == 0) {
318*41003Swilliam 		(void) splx(s);
319*41003Swilliam 		return;
320*41003Swilliam 	}
321*41003Swilliam 	sc->wd_flags |= WDF_TXBUSY;
322*41003Swilliam 	(void) splx(s);
323*41003Swilliam 
324*41003Swilliam 	/*
325*41003Swilliam 	 * Copy the mbuf chain into the transmit buffer
326*41003Swilliam 	 */
327*41003Swilliam 	buffer = sc->wd_vmem_addr;
328*41003Swilliam 	for (m0 = m; m != 0; m = m->m_next) {
329*41003Swilliam 		bcopy(mtod(m, caddr_t), buffer, m->m_len);
330*41003Swilliam 		buffer += m->m_len;
331*41003Swilliam         	len += m->m_len;
332*41003Swilliam 	}
333*41003Swilliam 
334*41003Swilliam 	/*
335*41003Swilliam 	 * If this was a broadcast packet loop it
336*41003Swilliam 	 * back because the hardware can't hear its own
337*41003Swilliam 	 * transmits.
338*41003Swilliam 	 */
339*41003Swilliam 	if (bcmp((caddr_t)(mtod(m0, struct ether_header *)->ether_dhost),
340*41003Swilliam 	   (caddr_t)etherbroadcastaddr,
341*41003Swilliam 	   sizeof(etherbroadcastaddr)) == 0) {
342*41003Swilliam 		wdread(sc, m0);
343*41003Swilliam 	} else {
344*41003Swilliam 		m_freem(m0);
345*41003Swilliam 	}
346*41003Swilliam 
347*41003Swilliam 	/*
348*41003Swilliam 	 * Init transmit length registers, and set transmit start flag.
349*41003Swilliam 	 */
350*41003Swilliam 	s = splhi();
351*41003Swilliam 	len = MAX(len, ETHER_MIN_LEN);
352*41003Swilliam 	wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
353*41003Swilliam 	wdcmd.cs_ps = 0;
354*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
355*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_TBCR0, len & 0xff);
356*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_TBCR1, len >> 8);
357*41003Swilliam 	wdcmd.cs_txp = 1;
358*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
359*41003Swilliam 	(void) splx(s);
360*41003Swilliam }
361*41003Swilliam 
362*41003Swilliam /*
363*41003Swilliam  * Ethernet interface interrupt processor
364*41003Swilliam  */
365*41003Swilliam wdintr(unit)
366*41003Swilliam 	int unit;
367*41003Swilliam {
368*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
369*41003Swilliam 	union wd_command wdcmd;
370*41003Swilliam 	union wd_interrupt wdisr;
371*41003Swilliam 	int s;
372*41003Swilliam 
373*41003Swilliam 	/* disable onboard interrupts, then get interrupt status */
374*41003Swilliam 	s = splhi();
375*41003Swilliam 	wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
376*41003Swilliam 	wdcmd.cs_ps = 0;
377*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
378*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_IMR, 0);
379*41003Swilliam 	wdisr.is_byte = INB(sc->wd_io_nic_addr + WD_P0_ISR);
380*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_ISR, 0xff);
381*41003Swilliam 	(void) splx(s);
382*41003Swilliam 
383*41003Swilliam 	/* transmit error */
384*41003Swilliam 	if (wdisr.is_txe) {
385*41003Swilliam 		/* need to read these registers to clear status */
386*41003Swilliam 		sc->wd_if.if_collisions +=
387*41003Swilliam 		    INB(sc->wd_io_nic_addr + WD_P0_TBCR0);
388*41003Swilliam 		++sc->wd_if.if_oerrors;
389*41003Swilliam 	}
390*41003Swilliam 
391*41003Swilliam 	/* receiver error */
392*41003Swilliam 	if (wdisr.is_rxe) {
393*41003Swilliam 		/* need to read these registers to clear status */
394*41003Swilliam 		(void) INB(sc->wd_io_nic_addr + 0xD);
395*41003Swilliam 		(void) INB(sc->wd_io_nic_addr + 0xE);
396*41003Swilliam 		(void) INB(sc->wd_io_nic_addr + 0xF);
397*41003Swilliam 		++sc->wd_if.if_ierrors;
398*41003Swilliam 	}
399*41003Swilliam 
400*41003Swilliam 	/* normal transmit complete */
401*41003Swilliam 	if (wdisr.is_ptx)
402*41003Swilliam 		wdtint (unit);
403*41003Swilliam 
404*41003Swilliam 	/* normal receive notification */
405*41003Swilliam 	if (wdisr.is_prx)
406*41003Swilliam 		wdrint (unit);
407*41003Swilliam 
408*41003Swilliam 	/* try to start transmit */
409*41003Swilliam 	wdstart(unit);
410*41003Swilliam 
411*41003Swilliam 	/* re-enable onboard interrupts */
412*41003Swilliam 	wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
413*41003Swilliam 	wdcmd.cs_ps = 0;
414*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
415*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
416*41003Swilliam }
417*41003Swilliam 
418*41003Swilliam /*
419*41003Swilliam  * Ethernet interface transmit interrupt.
420*41003Swilliam  */
421*41003Swilliam wdtint(unit)
422*41003Swilliam 	int unit;
423*41003Swilliam {
424*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
425*41003Swilliam 
426*41003Swilliam 	/*
427*41003Swilliam 	 * Do some statistics (assume page zero of NIC mapped in)
428*41003Swilliam 	 */
429*41003Swilliam 	sc->wd_flags &= ~WDF_TXBUSY;
430*41003Swilliam 	sc->wd_if.if_timer = 0;
431*41003Swilliam 	++sc->wd_if.if_opackets;
432*41003Swilliam 	sc->wd_if.if_collisions += INB(sc->wd_io_nic_addr + WD_P0_TBCR0);
433*41003Swilliam }
434*41003Swilliam 
435*41003Swilliam /*
436*41003Swilliam  * Ethernet interface receiver interrupt.
437*41003Swilliam  */
438*41003Swilliam wdrint(unit)
439*41003Swilliam 	int unit;
440*41003Swilliam {
441*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
442*41003Swilliam 	register struct mbuf **m;
443*41003Swilliam 	int mlen, len, count;
444*41003Swilliam 	u_char bnry, curr;
445*41003Swilliam 	union wd_command wdcmd;
446*41003Swilliam 	struct wd_ring *wdr;
447*41003Swilliam 	struct mbuf *m0;
448*41003Swilliam 	caddr_t pkt, endp;
449*41003Swilliam 
450*41003Swilliam 	/*
451*41003Swilliam 	 * Traverse the receive ring looking for packets to pass back.
452*41003Swilliam 	 * The search is complete when we find a descriptor not in use.
453*41003Swilliam 	 */
454*41003Swilliam 	wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
455*41003Swilliam 	wdcmd.cs_ps = 0;
456*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
457*41003Swilliam 	bnry = INB(sc->wd_io_nic_addr + WD_P0_BNRY);
458*41003Swilliam 	wdcmd.cs_ps = 1;
459*41003Swilliam 	OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
460*41003Swilliam 	curr = INB(sc->wd_io_nic_addr + WD_P1_CURR);
461*41003Swilliam 	while (bnry != curr)
462*41003Swilliam 	{
463*41003Swilliam 		/* get pointer to this buffer header structure */
464*41003Swilliam 		wdr = (struct wd_ring *)(sc->wd_vmem_addr + (bnry << 8));
465*41003Swilliam         	len = wdr->wd_count - 4;	/* count includes CRC */
466*41003Swilliam 		pkt = (caddr_t)(wdr + 1) - 2;	/* 2 - word align pkt data */
467*41003Swilliam         	count = len + 2;		/* copy two extra bytes */
468*41003Swilliam 		endp = (caddr_t)(sc->wd_vmem_addr + sc->wd_vmem_size);
469*41003Swilliam 		++sc->wd_if.if_ipackets;
470*41003Swilliam 
471*41003Swilliam 		/* pull packet out of dual ported RAM */
472*41003Swilliam 		m = &m0; m0 = 0;
473*41003Swilliam 		while (count > 0)
474*41003Swilliam 		{
475*41003Swilliam 		    /* drop chain if can't get another buffer */
476*41003Swilliam 		    MGET(*m, M_DONTWAIT, MT_DATA);
477*41003Swilliam 		    if (*m == 0)
478*41003Swilliam 		    {
479*41003Swilliam 			m_freem(m0);
480*41003Swilliam 			goto outofbufs;
481*41003Swilliam 		    }
482*41003Swilliam 
483*41003Swilliam 		    /* fill mbuf and attach to packet list */
484*41003Swilliam 		    mlen = MIN(MLEN, count);
485*41003Swilliam 		    mlen = MIN(mlen, endp - pkt);
486*41003Swilliam 		    bcopy(pkt, mtod(*m, caddr_t), mlen);
487*41003Swilliam 		    (*m)->m_len = mlen;
488*41003Swilliam 		    m = &((*m)->m_next);
489*41003Swilliam 		    pkt += mlen;
490*41003Swilliam 		    count -= mlen;
491*41003Swilliam 
492*41003Swilliam 		    /* wrap memory pointer around circ buffer */
493*41003Swilliam 		    if (pkt == endp)
494*41003Swilliam 			pkt = (caddr_t)sc->wd_vmem_ring;
495*41003Swilliam 		}
496*41003Swilliam 
497*41003Swilliam 		/* skip aligment bytes, send packet up to higher levels */
498*41003Swilliam 		if (m0 != 0)
499*41003Swilliam 		{
500*41003Swilliam 		    m0->m_off += 2;
501*41003Swilliam 		    wdread(sc, m0);
502*41003Swilliam 		}
503*41003Swilliam 
504*41003Swilliam outofbufs:
505*41003Swilliam 		/* advance on chip Boundry register */
506*41003Swilliam 		bnry = wdr->wd_next_packet;
507*41003Swilliam 		wdcmd.cs_byte = INB(sc->wd_io_nic_addr + WD_P0_COMMAND);
508*41003Swilliam 		wdcmd.cs_ps = 0;
509*41003Swilliam 		OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
510*41003Swilliam 
511*41003Swilliam 		/* watch out for NIC overflow, reset Boundry if invalid */
512*41003Swilliam 		if ((bnry - 1) < WD_TXBUF_SIZE) {
513*41003Swilliam #ifdef notdef
514*41003Swilliam 		    wdreset(unit, 0);
515*41003Swilliam 		    break;
516*41003Swilliam #else
517*41003Swilliam 		    OUTB(sc->wd_io_nic_addr + WD_P0_BNRY,
518*41003Swilliam 			(sc->wd_vmem_size / WD_PAGE_SIZE) - 1);
519*41003Swilliam #endif
520*41003Swilliam 		}
521*41003Swilliam 		OUTB(sc->wd_io_nic_addr + WD_P0_BNRY, bnry - 1);
522*41003Swilliam 
523*41003Swilliam 		/* refresh our copy of CURR */
524*41003Swilliam 		wdcmd.cs_ps = 1;
525*41003Swilliam 		OUTB(sc->wd_io_nic_addr + WD_P0_COMMAND, wdcmd.cs_byte);
526*41003Swilliam 		curr = INB(sc->wd_io_nic_addr + WD_P1_CURR);
527*41003Swilliam 	}
528*41003Swilliam }
529*41003Swilliam 
530*41003Swilliam /*
531*41003Swilliam  * Ethernet output routine.
532*41003Swilliam  * Encapsulate a packet of type family for the local net.
533*41003Swilliam  */
534*41003Swilliam wdoutput(ifp, m0, dst)
535*41003Swilliam 	struct ifnet *ifp;
536*41003Swilliam 	struct mbuf *m0;
537*41003Swilliam 	struct sockaddr *dst;
538*41003Swilliam {
539*41003Swilliam 	int type, s, error;
540*41003Swilliam 	u_char edst[6];
541*41003Swilliam 	struct in_addr idst;
542*41003Swilliam 	register struct wd_softc *sc = &wd_softc[ifp->if_unit];
543*41003Swilliam 	register struct mbuf *m = m0;
544*41003Swilliam 	register struct ether_header *eh;
545*41003Swilliam 	int usetrailers;
546*41003Swilliam 
547*41003Swilliam 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
548*41003Swilliam 		error = ENETDOWN;
549*41003Swilliam 		goto bad;
550*41003Swilliam 	}
551*41003Swilliam 
552*41003Swilliam 	switch (dst->sa_family) {
553*41003Swilliam 
554*41003Swilliam #ifdef INET
555*41003Swilliam 	case AF_INET:
556*41003Swilliam 		/* Note: we ignore usetrailers */
557*41003Swilliam 		idst = ((struct sockaddr_in *)dst)->sin_addr;
558*41003Swilliam 		if (!arpresolve(&sc->wd_ac, m, &idst, edst, &usetrailers))
559*41003Swilliam 			return (0);	/* if not yet resolved */
560*41003Swilliam 		type = ETHERTYPE_IP;
561*41003Swilliam 		break;
562*41003Swilliam #endif
563*41003Swilliam #ifdef NS
564*41003Swilliam 	case AF_NS:
565*41003Swilliam 		type = ETHERTYPE_NS;
566*41003Swilliam  		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
567*41003Swilliam 		    (caddr_t)edst, sizeof (edst));
568*41003Swilliam 		break;
569*41003Swilliam #endif
570*41003Swilliam 
571*41003Swilliam 
572*41003Swilliam 	case AF_UNSPEC:
573*41003Swilliam 		eh = (struct ether_header *)dst->sa_data;
574*41003Swilliam  		bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
575*41003Swilliam 		type = eh->ether_type;
576*41003Swilliam 		break;
577*41003Swilliam 
578*41003Swilliam 	default:
579*41003Swilliam 		printf("wd%d: can't handle af%d\n", ifp->if_unit,
580*41003Swilliam 			dst->sa_family);
581*41003Swilliam 		error = EAFNOSUPPORT;
582*41003Swilliam 		goto bad;
583*41003Swilliam 	}
584*41003Swilliam 
585*41003Swilliam 	/*
586*41003Swilliam 	 * Add local net header.  If no space in first mbuf,
587*41003Swilliam 	 * allocate another.
588*41003Swilliam 	 */
589*41003Swilliam 	if (m->m_off > MMAXOFF || MMINOFF + ETHER_HDR_SIZE > m->m_off) {
590*41003Swilliam 		m = m_get(M_DONTWAIT, MT_HEADER);
591*41003Swilliam 		if (m == 0) {
592*41003Swilliam 			error = ENOBUFS;
593*41003Swilliam 			goto bad;
594*41003Swilliam 		}
595*41003Swilliam 		m->m_next = m0;
596*41003Swilliam 		m->m_off = MMINOFF;
597*41003Swilliam 		m->m_len = ETHER_HDR_SIZE;
598*41003Swilliam 	} else {
599*41003Swilliam 		m->m_off -= ETHER_HDR_SIZE;
600*41003Swilliam 		m->m_len += ETHER_HDR_SIZE;
601*41003Swilliam 	}
602*41003Swilliam 	eh = mtod(m, struct ether_header *);
603*41003Swilliam 	eh->ether_type = htons((u_short)type);
604*41003Swilliam  	bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
605*41003Swilliam  	bcopy((caddr_t)sc->wd_addr, (caddr_t)eh->ether_shost,
606*41003Swilliam 		sizeof (sc->wd_addr));
607*41003Swilliam 
608*41003Swilliam 	/*
609*41003Swilliam 	 * Queue message on interface, and start output if interface
610*41003Swilliam 	 * not yet active.
611*41003Swilliam 	 */
612*41003Swilliam 	s = splimp();
613*41003Swilliam 	if (IF_QFULL(&ifp->if_snd)) {
614*41003Swilliam 		IF_DROP(&ifp->if_snd);
615*41003Swilliam 		(void) splx(s);
616*41003Swilliam 		m_freem(m);
617*41003Swilliam 		return (ENOBUFS);
618*41003Swilliam 	}
619*41003Swilliam 	IF_ENQUEUE(&ifp->if_snd, m);
620*41003Swilliam 	(void) splx(s);
621*41003Swilliam 	wdstart(ifp->if_unit);
622*41003Swilliam 	return (0);
623*41003Swilliam 
624*41003Swilliam bad:
625*41003Swilliam 	m_freem(m0);
626*41003Swilliam 	return (error);
627*41003Swilliam }
628*41003Swilliam 
629*41003Swilliam /*
630*41003Swilliam  * Process an ioctl request.
631*41003Swilliam  */
632*41003Swilliam wdioctl(ifp, cmd, data)
633*41003Swilliam 	register struct ifnet *ifp;
634*41003Swilliam 	int cmd;
635*41003Swilliam 	caddr_t data;
636*41003Swilliam {
637*41003Swilliam 	struct wd_softc *sc = &wd_softc[ifp->if_unit];
638*41003Swilliam 	struct ifaddr *ifa = (struct ifaddr *)data;
639*41003Swilliam 	int s = splimp(), error = 0;
640*41003Swilliam 
641*41003Swilliam 	switch (cmd) {
642*41003Swilliam 
643*41003Swilliam 	case SIOCSIFADDR:
644*41003Swilliam 		ifp->if_flags |= IFF_UP;
645*41003Swilliam 		wdinit(ifp->if_unit);
646*41003Swilliam 		switch(ifa->ifa_addr.sa_family) {
647*41003Swilliam #ifdef INET
648*41003Swilliam 		case AF_INET:
649*41003Swilliam 			((struct arpcom *)ifp)->ac_ipaddr =
650*41003Swilliam 				IA_SIN(ifa)->sin_addr;
651*41003Swilliam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
652*41003Swilliam 			break;
653*41003Swilliam #endif
654*41003Swilliam #ifdef NS
655*41003Swilliam 		case AF_NS:
656*41003Swilliam 		    {
657*41003Swilliam 			register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
658*41003Swilliam 
659*41003Swilliam 			if (ns_nullhost(*ina))
660*41003Swilliam 				ina->x_host = *(union ns_host *)(sc->wd_addr);
661*41003Swilliam 			else
662*41003Swilliam 				wdsetaddr(ina->x_host.c_host, ifp->if_unit);
663*41003Swilliam 			break;
664*41003Swilliam 		    }
665*41003Swilliam #endif
666*41003Swilliam 		}
667*41003Swilliam 		break;
668*41003Swilliam 
669*41003Swilliam 	case SIOCSIFFLAGS:
670*41003Swilliam 		if (((ifp->if_flags & IFF_UP) == 0) &&
671*41003Swilliam 		   (sc->wd_flags & WDF_RUNNING)) {
672*41003Swilliam 			wdstop(ifp->if_unit);
673*41003Swilliam 		} else if (((ifp->if_flags & IFF_UP) == IFF_UP) &&
674*41003Swilliam 		   ((sc->wd_flags & WDF_RUNNING) == 0))
675*41003Swilliam 			wdinit(ifp->if_unit);
676*41003Swilliam 		break;
677*41003Swilliam 
678*41003Swilliam 	default:
679*41003Swilliam 		error = EINVAL;
680*41003Swilliam 
681*41003Swilliam 	}
682*41003Swilliam 	(void) splx(s);
683*41003Swilliam 	return (error);
684*41003Swilliam }
685*41003Swilliam 
686*41003Swilliam /*
687*41003Swilliam  * set ethernet address for unit
688*41003Swilliam  */
689*41003Swilliam wdsetaddr(physaddr, unit)
690*41003Swilliam 	u_char *physaddr;
691*41003Swilliam 	int unit;
692*41003Swilliam {
693*41003Swilliam 	register struct wd_softc *sc = &wd_softc[unit];
694*41003Swilliam 	register int i;
695*41003Swilliam 
696*41003Swilliam 	/*
697*41003Swilliam 	 * Rewrite ethernet address, and then force restart of NIC
698*41003Swilliam 	 */
699*41003Swilliam 	for (i = 0; i < ETHER_ADDR_LEN; i++)
700*41003Swilliam 		sc->wd_addr[i] = physaddr[i];
701*41003Swilliam 	sc->wd_flags &= ~WDF_RUNNING;
702*41003Swilliam 	wdinit(unit);
703*41003Swilliam }
704*41003Swilliam 
705*41003Swilliam /*
706*41003Swilliam  * Pass a packet to the higher levels.
707*41003Swilliam  * NO TRAILER PROTOCOL!
708*41003Swilliam  */
709*41003Swilliam wdread(sc, m)
710*41003Swilliam 	register struct wd_softc *sc;
711*41003Swilliam     	struct mbuf *m;
712*41003Swilliam {
713*41003Swilliam 	struct ether_header *eh;
714*41003Swilliam 	int scn, type, s;
715*41003Swilliam 	struct ifqueue *inq;
716*41003Swilliam 
717*41003Swilliam 	/*
718*41003Swilliam 	 * Get ethernet protocol type out of ether header
719*41003Swilliam 	 */
720*41003Swilliam 	eh = mtod(m, struct ether_header *);
721*41003Swilliam 	type = ntohs((u_short)eh->ether_type);
722*41003Swilliam 
723*41003Swilliam 	/*
724*41003Swilliam 	 * Drop ethernet header
725*41003Swilliam 	 */
726*41003Swilliam 	m->m_off += ETHER_HDR_SIZE;
727*41003Swilliam 	m->m_len -= ETHER_HDR_SIZE;
728*41003Swilliam 
729*41003Swilliam 	/*
730*41003Swilliam 	 * Insert ifp pointer at start of packet
731*41003Swilliam 	 */
732*41003Swilliam 	m->m_off -= sizeof (struct ifnet *);
733*41003Swilliam 	m->m_len += sizeof (struct ifnet *);
734*41003Swilliam 	*(mtod(m, struct ifnet **)) = &sc->wd_if;
735*41003Swilliam 
736*41003Swilliam 	switch (type) {
737*41003Swilliam 
738*41003Swilliam #ifdef INET
739*41003Swilliam 	case ETHERTYPE_IP:
740*41003Swilliam 		scn = NETISR_IP;
741*41003Swilliam 		inq = &ipintrq;
742*41003Swilliam 		break;
743*41003Swilliam 
744*41003Swilliam 	case ETHERTYPE_ARP:
745*41003Swilliam 		arpinput(&sc->wd_ac, m);
746*41003Swilliam 		return;
747*41003Swilliam #endif
748*41003Swilliam #ifdef NS
749*41003Swilliam 	case ETHERTYPE_NS:
750*41003Swilliam 		scn = NETISR_NS;
751*41003Swilliam 		inq = &nsintrq;
752*41003Swilliam 		break;
753*41003Swilliam 
754*41003Swilliam #endif
755*41003Swilliam 
756*41003Swilliam 	default:
757*41003Swilliam 		m_freem(m);
758*41003Swilliam 		return;
759*41003Swilliam 	}
760*41003Swilliam 
761*41003Swilliam 	s = splimp();
762*41003Swilliam 	if (IF_QFULL(inq)) {
763*41003Swilliam 		IF_DROP(inq);
764*41003Swilliam 		m_freem(m);
765*41003Swilliam 	} else
766*41003Swilliam 		IF_ENQUEUE(inq, m);
767*41003Swilliam 	schednetisr(scn);
768*41003Swilliam 	(void) splx(s);
769*41003Swilliam }
770*41003Swilliam 
771*41003Swilliam #endif
772