386BSD 0.1 development
[unix-history] / usr / src / sys.386bsd / i386 / isa / if_we.c
CommitLineData
edce34dd
WJ
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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)if_we.c 7.3 (Berkeley) 5/21/91
37 */
38
39/*
40 * Modification history
41 *
42 * 8/28/89 - Initial version(if_wd.c), Tim L Tucker
43 */
44
45#include "we.h"
46#if NWE > 0
47/*
48 * Western Digital 8003 ethernet/starlan adapter
49 *
50 * Supports the following interface cards:
51 * WD8003E, WD8003EBT, WD8003S, WD8003SBT, WD8013EBT
52 *
53 * The Western Digital card is one of many AT/MCA ethernet interfaces
54 * based on the National DS8390 Network Interface chip set.
55 */
56#include "param.h"
57#include "mbuf.h"
58#include "socket.h"
59#include "ioctl.h"
60#include "errno.h"
61#include "syslog.h"
62
63#include "net/if.h"
64#include "net/netisr.h"
65
66#ifdef INET
67#include "netinet/in.h"
68#include "netinet/in_systm.h"
69#include "netinet/in_var.h"
70#include "netinet/ip.h"
71#include "netinet/if_ether.h"
72#endif
73
74#ifdef NS
75#include "netns/ns.h"
76#include "netns/ns_if.h"
77#endif
78
79#include "i386/isa/isa.h"
80#include "i386/isa/if_wereg.h"
81#include "i386/isa/isa_device.h"
82#include "i386/isa/icu.h"
83
84/*
85 * This constant should really be 60 because the we adds 4 bytes of crc.
86 * However when set to 60 our packets are ignored by deunas , 3coms are
87 * okay ??????????????????????????????????????????
88 */
89#define ETHER_MIN_LEN 64
90#define ETHER_ADDR_LEN 6
91#define ETHER_HDR_SIZE 14
92
93/*
94 * Ethernet software status per interface.
95 *
96 * Each interface is referenced by a network interface structure,
97 * qe_if, which the routing code uses to locate the interface.
98 * This structure contains the output queue for the interface, its address, ...
99 */
100struct we_softc {
101 struct arpcom we_ac; /* Ethernet common part */
102#define we_if we_ac.ac_if /* network-visible interface */
103#define we_addr we_ac.ac_enaddr /* hardware Ethernet address */
104
105 u_char we_flags; /* software state */
106#define WDF_RUNNING 0x01
107#define WDF_TXBUSY 0x02
108
109 u_char we_type; /* interface type code */
110 u_short we_vector; /* interrupt vector */
111 short we_io_ctl_addr; /* i/o bus address, control */
112 short we_io_nic_addr; /* i/o bus address, DS8390 */
113
114 caddr_t we_vmem_addr; /* card RAM virtual memory base */
115 u_long we_vmem_size; /* card RAM bytes */
116 caddr_t we_vmem_ring; /* receive ring RAM vaddress */
117 caddr_t we_vmem_end; /* receive ring RAM end */
118} we_softc[NWE];
119
120int weprobe(), weattach(), weintr(), westart();
121int weinit(), ether_output(), weioctl(), wereset(), wewatchdog();
122
123struct isa_driver wedriver = {
124 weprobe, weattach, "we",
125};
126
127static unsigned short wemask[] =
128 { IRQ9, IRQ3, IRQ5, IRQ7, IRQ10, IRQ11, IRQ15, IRQ4 };
129
130/*
131 * Probe the WD8003 to see if its there
132 */
133weprobe(is)
134 struct isa_device *is;
135{
136 register int i;
137 register struct we_softc *sc = &we_softc[is->id_unit];
138 union we_mem_sel wem;
139 u_char sum;
140
141 /* reset card to force it into a known state. */
142 outb(is->id_iobase, 0x80);
143 DELAY(100);
144 outb(is->id_iobase, 0x00);
145 /* wait in the case this card is reading it's EEROM */
146 DELAY(5000);
147
148 /*
149 * Here we check the card ROM, if the checksum passes, and the
150 * type code and ethernet address check out, then we know we have
151 * a wd8003 card.
152 *
153 * Autoconfiguration: No warning message is printed on error.
154 */
155 for (sum = 0, i = 0; i < 8; ++i)
156 sum += inb(is->id_iobase + WD_ROM_OFFSET + i);
157 if (sum != WD_CHECKSUM)
158 return (0);
159 sc->we_type = inb(is->id_iobase + WD_ROM_OFFSET + 6);
160#ifdef nope
161 if ((sc->we_type & WD_REVMASK) != 2 /* WD8003E or WD8003S */
162 && (sc->we_type & WD_REVMASK) != 4 /* WD8003EBT */
163 && (sc->we_type & WD_REVMASK) != 6) /* WD8003ELB? */
164 return (0);
165#endif
166/*printf("type %x ", sc->we_type);*/
167 if (sc->we_type & WD_SOFTCONFIG) {
168 int iv = inb(is->id_iobase + 1) & 4 |
169 ((inb(is->id_iobase+4) & 0x60) >> 5);
170/*printf("iv %d ", iv);*/
171 if (wemask[iv] != is->id_irq)
172 return(0);
173 outb(is->id_iobase+4, inb(is->id_iobase+4) | 0x80);
174 }
175
176 /*
177 * Setup card RAM area and i/o addresses
178 * Kernel Virtual to segment C0000-DFFFF?????
179 */
180 sc->we_io_ctl_addr = is->id_iobase;
181 sc->we_io_nic_addr = sc->we_io_ctl_addr + WD_NIC_OFFSET;
182 sc->we_vector = is->id_irq;
183 sc->we_vmem_addr = (caddr_t)is->id_maddr;
184 sc->we_vmem_size = is->id_msize;
185 sc->we_vmem_ring = sc->we_vmem_addr + (WD_PAGE_SIZE * WD_TXBUF_SIZE);
186 sc->we_vmem_end = sc->we_vmem_addr + is->id_msize;
187
188 /*
189 * Save board ROM station address
190 */
191 for (i = 0; i < ETHER_ADDR_LEN; ++i)
192 sc->we_addr[i] = inb(sc->we_io_ctl_addr + WD_ROM_OFFSET + i);
193
194 /*
195 * Mapin interface memory, setup memory select register
196 */
197 wem.ms_addr = kvtop(sc->we_vmem_addr) >> 13;
198 wem.ms_enable = 1;
199 wem.ms_reset = 0;
200 outb(sc->we_io_ctl_addr, wem.ms_byte);
201
202 /*
203 * clear interface memory, then sum to make sure its valid
204 */
205 for (i = 0; i < sc->we_vmem_size; ++i)
206 sc->we_vmem_addr[i] = 0x0;
207 for (sum = 0, i = 0; i < sc->we_vmem_size; ++i)
208 sum += sc->we_vmem_addr[i];
209 if (sum != 0x0) {
210 printf("we%d: wd8003 dual port RAM address error\n", is->id_unit);
211 return (0);
212 }
213
214 return (WD_IO_PORTS);
215}
216
217/*
218 * Interface exists: make available by filling in network interface
219 * record. System will initialize the interface when it is ready
220 * to accept packets.
221 */
222weattach(is)
223 struct isa_device *is;
224{
225 register struct we_softc *sc = &we_softc[is->id_unit];
226 register struct ifnet *ifp = &sc->we_if;
227 union we_command wecmd;
228
229 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
230 wecmd.cs_stp = 1;
231 wecmd.cs_sta = 0;
232 wecmd.cs_ps = 0;
233 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
234 /*
235 * Initialize ifnet structure
236 */
237 ifp->if_unit = is->id_unit;
238 ifp->if_name = "we" ;
239 ifp->if_mtu = ETHERMTU;
240 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS ;
241 ifp->if_init = weinit;
242 ifp->if_output = ether_output;
243 ifp->if_start = westart;
244 ifp->if_ioctl = weioctl;
245 ifp->if_reset = wereset;
246 ifp->if_watchdog = wewatchdog;
247 if_attach(ifp);
248
249 /*
250 * Banner...
251 */
252 printf(" %s address %s",
253 (sc->we_type & WD_ETHERNET) ? "ethernet" : "starlan",
254 ether_sprintf(sc->we_addr));
255}
256
257/*
258 * Reset of interface.
259 */
260wereset(unit, uban)
261 int unit, uban;
262{
263 if (unit >= NWE)
264 return;
265 printf("we%d: reset\n", unit);
266/* we_softc[unit].we_flags &= ~WDF_RUNNING; */
267 weinit(unit);
268}
269
270/*
271 * Take interface offline.
272 */
273westop(unit)
274 int unit;
275{
276 register struct we_softc *sc = &we_softc[unit];
277 union we_command wecmd;
278 int s;
279
280 /*
281 * Shutdown DS8390
282 */
283 s = splimp();
284 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
285 wecmd.cs_stp = 1;
286 wecmd.cs_sta = 0;
287 wecmd.cs_ps = 0;
288 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
289 (void) splx(s);
290}
291
292wewatchdog(unit) {
293
294 log(LOG_WARNING,"we%d: soft reset\n", unit);
295 westop(unit);
296 weinit(unit);
297}
298
299static Bdry;
300/*
301 * Initialization of interface (really just DS8390).
302 */
303weinit(unit)
304 int unit;
305{
306 register struct we_softc *sc = &we_softc[unit];
307 register struct ifnet *ifp = &sc->we_if;
308 union we_command wecmd;
309 int i, s;
310
311 /* address not known */
312 if (ifp->if_addrlist == (struct ifaddr *)0)
313 return;
314
315 /* already running */
316 /*if (ifp->if_flags & IFF_RUNNING) return; */
317
318 /*
319 * Initialize DS8390 in order given in NSC NIC manual.
320 * this is stock code...please see the National manual for details.
321 */
322 s = splhigh();
323Bdry = 0;
324 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
325 wecmd.cs_stp = 1;
326 wecmd.cs_sta = 0;
327 wecmd.cs_ps = 0;
328 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
329 outb(sc->we_io_nic_addr + WD_P0_DCR, WD_D_CONFIG);
330 outb(sc->we_io_nic_addr + WD_P0_RBCR0, 0);
331 outb(sc->we_io_nic_addr + WD_P0_RBCR1, 0);
332 outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_MON);
333 outb(sc->we_io_nic_addr + WD_P0_TCR, WD_T_CONFIG);
334 outb(sc->we_io_nic_addr + WD_P0_TPSR, 0);
335 outb(sc->we_io_nic_addr + WD_P0_PSTART, WD_TXBUF_SIZE);
336 outb(sc->we_io_nic_addr + WD_P0_PSTOP,
337 sc->we_vmem_size / WD_PAGE_SIZE);
338 outb(sc->we_io_nic_addr + WD_P0_BNRY, WD_TXBUF_SIZE);
339 outb(sc->we_io_nic_addr + WD_P0_ISR, 0xff);
340 outb(sc->we_io_nic_addr + WD_P0_IMR, WD_I_CONFIG);
341 wecmd.cs_ps = 1;
342 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
343 for (i = 0; i < ETHER_ADDR_LEN; ++i)
344 outb(sc->we_io_nic_addr + WD_P1_PAR0 + i, sc->we_addr[i]);
345 for (i = 0; i < ETHER_ADDR_LEN; ++i) /* == broadcast addr */
346 outb(sc->we_io_nic_addr + WD_P1_MAR0 + i, 0xff);
347 outb(sc->we_io_nic_addr + WD_P1_CURR, WD_TXBUF_SIZE);
348 wecmd.cs_ps = 0;
349 wecmd.cs_stp = 0;
350 wecmd.cs_sta = 1;
351 wecmd.cs_rd = 0x4;
352 outb(sc->we_io_nic_addr + WD_P1_COMMAND, wecmd.cs_byte);
353 outb(sc->we_io_nic_addr + WD_P0_RCR, WD_R_CONFIG);
354
355 /*
356 * Take the interface out of reset, program the vector,
357 * enable interrupts, and tell the world we are up.
358 */
359 ifp->if_flags |= IFF_RUNNING;
360 sc->we_flags &= ~WDF_TXBUSY;
361 (void) splx(s);
362 westart(ifp);
363}
364
365/*
366 * Start output on interface.
367 */
368westart(ifp)
369 struct ifnet *ifp;
370{
371 register struct we_softc *sc = &we_softc[ifp->if_unit];
372 struct mbuf *m0, *m;
373 register caddr_t buffer;
374 int len, s;
375 union we_command wecmd;
376
377 /*
378 * The DS8390 has only one transmit buffer, if it is busy we
379 * must wait until the transmit interrupt completes.
380 */
381 s = splhigh();
382 if (sc->we_flags & WDF_TXBUSY) {
383 (void) splx(s);
384 return;
385 }
386 IF_DEQUEUE(&sc->we_if.if_snd, m);
387 if (m == 0) {
388 (void) splx(s);
389 return;
390 }
391 sc->we_flags |= WDF_TXBUSY;
392 (void) splx(s);
393
394 /*
395 * Copy the mbuf chain into the transmit buffer
396 */
397 buffer = sc->we_vmem_addr;
398 len = 0;
399 for (m0 = m; m != 0; m = m->m_next) {
400 bcopy(mtod(m, caddr_t), buffer, m->m_len);
401 buffer += m->m_len;
402 len += m->m_len;
403 }
404
405 m_freem(m0);
406
407 /*
408 * Init transmit length registers, and set transmit start flag.
409 */
410 s = splhigh();
411 len = MAX(len, ETHER_MIN_LEN);
412 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
413 wecmd.cs_ps = 0;
414 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
415 outb(sc->we_io_nic_addr + WD_P0_TBCR0, len & 0xff);
416 outb(sc->we_io_nic_addr + WD_P0_TBCR1, len >> 8);
417 wecmd.cs_txp = 1;
418 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
419 (void) splx(s);
420}
421
422/*
423 * Ethernet interface interrupt processor
424 */
425weintr(unit)
426 int unit;
427{
428 register struct we_softc *sc = &we_softc[unit];
429 union we_command wecmd;
430 union we_interrupt weisr;
431
432 unit =0;
433
434 /* disable onboard interrupts, then get interrupt status */
435 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
436 wecmd.cs_ps = 0;
437 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
438 weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
439loop:
440 outb(sc->we_io_nic_addr + WD_P0_ISR, weisr.is_byte);
441
442 /* transmit error */
443 if (weisr.is_txe) {
444 /* need to read these registers to clear status */
445 sc->we_if.if_collisions +=
446 inb(sc->we_io_nic_addr + WD_P0_TBCR0);
447 ++sc->we_if.if_oerrors;
448 }
449
450 /* receiver error */
451 if (weisr.is_rxe) {
452 /* need to read these registers to clear status */
453 (void) inb(sc->we_io_nic_addr + 0xD);
454 (void) inb(sc->we_io_nic_addr + 0xE);
455 (void) inb(sc->we_io_nic_addr + 0xF);
456 ++sc->we_if.if_ierrors;
457 }
458
459 /* normal transmit complete */
460 if (weisr.is_ptx || weisr.is_txe)
461 wetint (unit);
462
463 /* normal receive notification */
464 if (weisr.is_prx || weisr.is_rxe)
465 werint (unit);
466
467 /* try to start transmit */
468 westart(&sc->we_if);
469
470 /* re-enable onboard interrupts */
471 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
472 wecmd.cs_ps = 0;
473 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
474 outb(sc->we_io_nic_addr + WD_P0_IMR, 0xff/*WD_I_CONFIG*/);
475 weisr.is_byte = inb(sc->we_io_nic_addr + WD_P0_ISR);
476 if (weisr.is_byte) goto loop;
477}
478
479/*
480 * Ethernet interface transmit interrupt.
481 */
482wetint(unit)
483 int unit;
484{
485 register struct we_softc *sc = &we_softc[unit];
486
487 /*
488 * Do some statistics (assume page zero of NIC mapped in)
489 */
490 sc->we_flags &= ~WDF_TXBUSY;
491 sc->we_if.if_timer = 0;
492 ++sc->we_if.if_opackets;
493 sc->we_if.if_collisions += inb(sc->we_io_nic_addr + WD_P0_TBCR0);
494}
495
496/*
497 * Ethernet interface receiver interrupt.
498 */
499werint(unit)
500 int unit;
501{
502 register struct we_softc *sc = &we_softc[unit];
503 u_char bnry, curr;
504 long len;
505 union we_command wecmd;
506 struct we_ring *wer;
507
508 /*
509 * Traverse the receive ring looking for packets to pass back.
510 * The search is complete when we find a descriptor not in use.
511 */
512 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
513 wecmd.cs_ps = 0;
514 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
515 bnry = inb(sc->we_io_nic_addr + WD_P0_BNRY);
516 wecmd.cs_ps = 1;
517 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
518 curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
519if(Bdry)
520 bnry =Bdry;
521
522 while (bnry != curr)
523 {
524 /* get pointer to this buffer header structure */
525 wer = (struct we_ring *)(sc->we_vmem_addr + (bnry << 8));
526
527 /* count includes CRC */
528 len = wer->we_count - 4;
529 if (len > 30 && len <= ETHERMTU+100
530 /*&& (*(char *)wer == 1 || *(char *) wer == 0x21)*/)
531 weread(sc, (caddr_t)(wer + 1), len);
532 else printf("reject %d", len);
533
534outofbufs:
535 wecmd.cs_byte = inb(sc->we_io_nic_addr + WD_P0_COMMAND);
536 wecmd.cs_ps = 0;
537 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
538
539 /* advance on chip Boundry register */
540 if((caddr_t) wer + WD_PAGE_SIZE - 1 > sc->we_vmem_end) {
541 bnry = WD_TXBUF_SIZE;
542 outb(sc->we_io_nic_addr + WD_P0_BNRY,
543 sc->we_vmem_size / WD_PAGE_SIZE-1);
544
545 } else {
546 if (len > 30 && len <= ETHERMTU+100)
547 bnry = wer->we_next_packet;
548 else bnry = curr;
549
550 /* watch out for NIC overflow, reset Boundry if invalid */
551 if ((bnry - 1) < WD_TXBUF_SIZE) {
552 outb(sc->we_io_nic_addr + WD_P0_BNRY,
553 (sc->we_vmem_size / WD_PAGE_SIZE) - 1);
554 bnry = WD_TXBUF_SIZE;
555 } else
556 outb(sc->we_io_nic_addr + WD_P0_BNRY, bnry-1);
557 }
558
559 /* refresh our copy of CURR */
560 wecmd.cs_ps = 1;
561 outb(sc->we_io_nic_addr + WD_P0_COMMAND, wecmd.cs_byte);
562 curr = inb(sc->we_io_nic_addr + WD_P1_CURR);
563 }
564Bdry = bnry;
565}
566
567/*
568 * Process an ioctl request.
569 */
570weioctl(ifp, cmd, data)
571 register struct ifnet *ifp;
572 int cmd;
573 caddr_t data;
574{
575 register struct ifaddr *ifa = (struct ifaddr *)data;
576 struct we_softc *sc = &we_softc[ifp->if_unit];
577 struct ifreq *ifr = (struct ifreq *)data;
578 int s = splimp(), error = 0;
579
580
581 switch (cmd) {
582
583 case SIOCSIFADDR:
584 ifp->if_flags |= IFF_UP;
585
586 switch (ifa->ifa_addr->sa_family) {
587#ifdef INET
588 case AF_INET:
589 weinit(ifp->if_unit); /* before arpwhohas */
590 ((struct arpcom *)ifp)->ac_ipaddr =
591 IA_SIN(ifa)->sin_addr;
592 arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
593 break;
594#endif
595#ifdef NS
596 case AF_NS:
597 {
598 register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
599
600 if (ns_nullhost(*ina))
601 ina->x_host = *(union ns_host *)(sc->ns_addr);
602 else {
603 /*
604 * The manual says we cant change the address
605 * while the receiver is armed,
606 * so reset everything
607 */
608 ifp->if_flags &= ~IFF_RUNNING;
609 bcopy((caddr_t)ina->x_host.c_host,
610 (caddr_t)sc->ns_addr, sizeof(sc->ns_addr));
611 }
612 weinit(ifp->if_unit); /* does ne_setaddr() */
613 break;
614 }
615#endif
616 default:
617 weinit(ifp->if_unit);
618 break;
619 }
620 break;
621
622 case SIOCSIFFLAGS:
623 if ((ifp->if_flags & IFF_UP) == 0 &&
624 ifp->if_flags & IFF_RUNNING) {
625 ifp->if_flags &= ~IFF_RUNNING;
626 westop(ifp->if_unit);
627 } else if (ifp->if_flags & IFF_UP &&
628 (ifp->if_flags & IFF_RUNNING) == 0)
629 weinit(ifp->if_unit);
630 break;
631
632#ifdef notdef
633 case SIOCGHWADDR:
634 bcopy((caddr_t)sc->sc_addr, (caddr_t) &ifr->ifr_data,
635 sizeof(sc->sc_addr));
636 break;
637#endif
638
639 default:
640 error = EINVAL;
641 }
642 splx(s);
643 return (error);
644}
645/*
646 * set ethernet address for unit
647 */
648wesetaddr(physaddr, unit)
649 u_char *physaddr;
650 int unit;
651{
652 register struct we_softc *sc = &we_softc[unit];
653 register int i;
654
655 /*
656 * Rewrite ethernet address, and then force restart of NIC
657 */
658 for (i = 0; i < ETHER_ADDR_LEN; i++)
659 sc->we_addr[i] = physaddr[i];
660 sc->we_flags &= ~WDF_RUNNING;
661 weinit(unit);
662}
663
664#define wedataaddr(sc, eh, off, type) \
665 ((type) ((caddr_t)((eh)+1)+(off) >= (sc)->we_vmem_end) ? \
666 (((caddr_t)((eh)+1)+(off))) - (sc)->we_vmem_end \
667 + (sc)->we_vmem_ring: \
668 ((caddr_t)((eh)+1)+(off)))
669/*
670 * Pass a packet to the higher levels.
671 * We deal with the trailer protocol here.
672 */
673weread(sc, buf, len)
674 register struct we_softc *sc;
675 char *buf;
676 int len;
677{
678 register struct ether_header *eh;
679 struct mbuf *m, *weget();
680 int off, resid;
681
682 /*
683 * Deal with trailer protocol: if type is trailer type
684 * get true type from first 16-bit word past data.
685 * Remember that type was trailer by setting off.
686 */
687 eh = (struct ether_header *)buf;
688 eh->ether_type = ntohs((u_short)eh->ether_type);
689 if (eh->ether_type >= ETHERTYPE_TRAIL &&
690 eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
691 off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
692 if (off >= ETHERMTU) return; /* sanity */
693 eh->ether_type = ntohs(*wedataaddr(sc, eh, off, u_short *));
694 resid = ntohs(*(wedataaddr(sc, eh, off+2, u_short *)));
695 if (off + resid > len) return; /* sanity */
696 len = off + resid;
697 } else off = 0;
698
699 len -= sizeof(struct ether_header);
700 if (len <= 0) return;
701
702 /*
703 * Pull packet off interface. Off is nonzero if packet
704 * has trailing header; neget will then force this header
705 * information to be at the front, but we still have to drop
706 * the type and length which are at the front of any trailer data.
707 */
708 m = weget(buf, len, off, &sc->we_if, sc);
709 if (m == 0) return;
710 ether_input(&sc->we_if, eh, m);
711}
712
713/*
714 * Supporting routines
715 */
716
717/*
718 * Pull read data off a interface.
719 * Len is length of data, with local net header stripped.
720 * Off is non-zero if a trailer protocol was used, and
721 * gives the offset of the trailer information.
722 * We copy the trailer information and then all the normal
723 * data into mbufs. When full cluster sized units are present
724 * we copy into clusters.
725 */
726struct mbuf *
727weget(buf, totlen, off0, ifp, sc)
728 caddr_t buf;
729 int totlen, off0;
730 struct ifnet *ifp;
731 struct we_softc *sc;
732{
733 struct mbuf *top, **mp, *m, *p;
734 int off = off0, len;
735 register caddr_t cp = buf;
736 char *epkt;
737 int tc =totlen;
738
739 buf += sizeof(struct ether_header);
740 cp = buf;
741 epkt = cp + totlen;
742
743 if (off) {
744 cp += off + 2 * sizeof(u_short);
745 totlen -= 2 * sizeof(u_short);
746 }
747
748 MGETHDR(m, M_DONTWAIT, MT_DATA);
749 if (m == 0)
750 return (0);
751 m->m_pkthdr.rcvif = ifp;
752 m->m_pkthdr.len = totlen;
753 m->m_len = MHLEN;
754
755 top = 0;
756 mp = &top;
757 while (totlen > 0) {
758 if (top) {
759 MGET(m, M_DONTWAIT, MT_DATA);
760 if (m == 0) {
761 m_freem(top);
762 return (0);
763 }
764 m->m_len = MLEN;
765 }
766 len = min(totlen, epkt - cp);
767 if (len >= MINCLSIZE) {
768 MCLGET(m, M_DONTWAIT);
769 if (m->m_flags & M_EXT)
770 m->m_len = len = min(len, MCLBYTES);
771 else
772 len = m->m_len;
773 } else {
774 /*
775 * Place initial small packet/header at end of mbuf.
776 */
777 if (len < m->m_len) {
778 if (top == 0 && len + max_linkhdr <= m->m_len)
779 m->m_data += max_linkhdr;
780 m->m_len = len;
781 } else
782 len = m->m_len;
783 }
784
785 totlen -= len;
786 /* only do up to end of buffer */
787 if (cp+len > sc->we_vmem_end) {
788 unsigned toend = sc->we_vmem_end - cp;
789
790 bcopy(cp, mtod(m, caddr_t), toend);
791 cp = sc->we_vmem_ring;
792 bcopy(cp, mtod(m, caddr_t)+toend, len - toend);
793 cp += len - toend;
794 epkt = cp + totlen;
795 } else {
796 bcopy(cp, mtod(m, caddr_t), (unsigned)len);
797 cp += len;
798 }
799 *mp = m;
800 mp = &m->m_next;
801 if (cp == epkt) {
802 cp = buf;
803 epkt = cp + tc;
804 }
805 }
806 return (top);
807}
808#endif