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