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