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 | |
6 | * Tim L. Tucker | |
7 | * | |
8 | * %sccs.include.noredist.c% | |
9 | * | |
10 | * @(#)if_we.c 5.1 (Berkeley) %G% | |
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 |