Commit | Line | Data |
---|---|---|
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 |
72 | struct 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 |
92 | int weprobe(), weattach(), weintr(), westart(); |
93 | int weinit(), ether_output(), weioctl(), wereset(), wewatchdog(); | |
4ad9a99e BJ |
94 | |
95 | struct isa_driver wedriver = { | |
96 | weprobe, weattach, "we", | |
97 | }; | |
11dcaca6 WN |
98 | |
99 | /* | |
100 | * Probe the WD8003 to see if it's there | |
101 | */ | |
4ad9a99e | 102 | weprobe(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 | 173 | weattach(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 | 211 | wereset(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 | 224 | westop(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 |
243 | wewatchdog(unit) { |
244 | ||
245 | log(LOG_WARNING,"we%d: soft reset\n", unit); | |
d0d290ad | 246 | westop(unit); |
d0d290ad WN |
247 | weinit(unit); |
248 | } | |
249 | ||
250 | static Bdry; | |
11dcaca6 | 251 | /* |
012ba128 | 252 | * Initialization of interface (really just DS8390). |
11dcaca6 | 253 | */ |
4ad9a99e | 254 | weinit(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 | 274 | Bdry = 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 |
319 | westart(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 | 376 | weintr(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 |
390 | loop: |
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 | 433 | wetint(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 | 450 | werint(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 | 470 | if(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 | |
485 | outofbufs: | |
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 | 515 | Bdry = bnry; |
11dcaca6 WN |
516 | } |
517 | ||
d0d290ad | 518 | #ifdef shit |
11dcaca6 | 519 | /* |
d0d290ad | 520 | * Process an ioctl request. |
11dcaca6 | 521 | */ |
d0d290ad WN |
522 | weioctl(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 | 580 | weioctl(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 | 658 | wesetaddr(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 | 683 | weread(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 | */ | |
736 | struct mbuf * | |
737 | weget(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 = ⊤ | |
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 |