Commit | Line | Data |
---|---|---|
cd4baa58 KS |
1 | /* |
2 | * Copyright (c) 1982 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | * | |
8011f5df | 6 | * @(#)if_ec.c 6.18 (Berkeley) %G% |
cd4baa58 | 7 | */ |
75334b2a BF |
8 | |
9 | #include "ec.h" | |
9a0b0c74 | 10 | #if NEC > 0 |
75334b2a BF |
11 | |
12 | /* | |
13 | * 3Com Ethernet Controller interface | |
14 | */ | |
961945a8 | 15 | #include "../machine/pte.h" |
75334b2a | 16 | |
a6e960e7 JB |
17 | #include "param.h" |
18 | #include "systm.h" | |
19 | #include "mbuf.h" | |
20 | #include "buf.h" | |
21 | #include "protosw.h" | |
22 | #include "socket.h" | |
0f487295 | 23 | #include "syslog.h" |
a6e960e7 JB |
24 | #include "vmmac.h" |
25 | #include "ioctl.h" | |
26 | #include "errno.h" | |
a9687d27 BJ |
27 | |
28 | #include "../net/if.h" | |
29 | #include "../net/netisr.h" | |
30 | #include "../net/route.h" | |
cd4baa58 KS |
31 | |
32 | #ifdef INET | |
d2cc167c BJ |
33 | #include "../netinet/in.h" |
34 | #include "../netinet/in_systm.h" | |
7f0e1e06 | 35 | #include "../netinet/in_var.h" |
d2cc167c | 36 | #include "../netinet/ip.h" |
8ae4cebd | 37 | #include "../netinet/if_ether.h" |
cd4baa58 KS |
38 | #endif |
39 | ||
cd4baa58 KS |
40 | #ifdef NS |
41 | #include "../netns/ns.h" | |
42 | #include "../netns/ns_if.h" | |
b1b3e868 | 43 | #endif |
a9687d27 BJ |
44 | |
45 | #include "../vax/cpu.h" | |
46 | #include "../vax/mtpr.h" | |
a6e960e7 JB |
47 | #include "if_ecreg.h" |
48 | #include "if_uba.h" | |
a9687d27 BJ |
49 | #include "../vaxuba/ubareg.h" |
50 | #include "../vaxuba/ubavar.h" | |
75334b2a | 51 | |
b6220e6c MK |
52 | #if CLSIZE == 2 |
53 | #define ECBUFSIZE 32 /* on-board memory, clusters */ | |
3f506470 | 54 | #endif |
3f506470 | 55 | |
b6220e6c | 56 | int ecubamem(), ecprobe(), ecattach(), ecrint(), ecxint(), eccollide(); |
75334b2a BF |
57 | struct uba_device *ecinfo[NEC]; |
58 | u_short ecstd[] = { 0 }; | |
59 | struct uba_driver ecdriver = | |
b6220e6c | 60 | { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo, 0, 0, 0, ecubamem }; |
75334b2a | 61 | |
1cd789fb | 62 | int ecinit(),ecioctl(),ecoutput(),ecreset(); |
39d536e6 | 63 | struct mbuf *ecget(); |
75334b2a | 64 | |
be515f6e BF |
65 | extern struct ifnet loif; |
66 | ||
75334b2a BF |
67 | /* |
68 | * Ethernet software status per interface. | |
69 | * | |
70 | * Each interface is referenced by a network interface structure, | |
71 | * es_if, which the routing code uses to locate the interface. | |
72 | * This structure contains the output queue for the interface, its address, ... | |
73 | * We also have, for each interface, a UBA interface structure, which | |
74 | * contains information about the UNIBUS resources held by the interface: | |
75 | * map registers, buffered data paths, etc. Information is cached in this | |
76 | * structure for use by the if_uba.c routines in running the interface | |
77 | * efficiently. | |
78 | */ | |
79 | struct ec_softc { | |
8ae4cebd SL |
80 | struct arpcom es_ac; /* common Ethernet structures */ |
81 | #define es_if es_ac.ac_if /* network-visible interface */ | |
82 | #define es_addr es_ac.ac_enaddr /* hardware Ethernet address */ | |
75334b2a | 83 | struct ifuba es_ifuba; /* UNIBUS resources */ |
75334b2a | 84 | short es_mask; /* mask for current output delay */ |
75334b2a | 85 | short es_oactive; /* is output active? */ |
39d536e6 | 86 | u_char *es_buf[16]; /* virtual addresses of buffers */ |
75334b2a BF |
87 | } ec_softc[NEC]; |
88 | ||
89 | /* | |
b6220e6c MK |
90 | * Configure on-board memory for an interface. |
91 | * Called from autoconfig and after a uba reset. | |
92 | * The address of the memory on the uba is supplied in the device flags. | |
75334b2a | 93 | */ |
b6220e6c MK |
94 | ecubamem(ui, uban) |
95 | register struct uba_device *ui; | |
75334b2a | 96 | { |
b6220e6c MK |
97 | register caddr_t ecbuf = (caddr_t) &umem[uban][ui->ui_flags]; |
98 | register struct ecdevice *addr = (struct ecdevice *)ui->ui_addr; | |
75334b2a | 99 | |
b6220e6c MK |
100 | /* |
101 | * Make sure csr is there (we run before ecprobe). | |
102 | */ | |
103 | if (badaddr((caddr_t)addr, 2)) | |
104 | return (-1); | |
105 | #if VAX780 | |
106 | if (cpu == VAX_780 && uba_hd[uban].uh_uba->uba_sr) { | |
107 | uba_hd[uban].uh_uba->uba_sr = uba_hd[uban].uh_uba->uba_sr; | |
108 | return (-1); | |
109 | } | |
75334b2a | 110 | #endif |
f5a616ae BF |
111 | /* |
112 | * Make sure memory is turned on | |
113 | */ | |
114 | addr->ec_rcr = EC_AROM; | |
a26646de | 115 | /* |
b6220e6c MK |
116 | * Tell the system that the board has memory here, so it won't |
117 | * attempt to allocate the addresses later. | |
a26646de | 118 | */ |
b6220e6c MK |
119 | if (ubamem(uban, ui->ui_flags, ECBUFSIZE*CLSIZE, 1) == 0) { |
120 | printf("ec%d: cannot reserve uba addresses\n", ui->ui_unit); | |
121 | addr->ec_rcr = EC_MDISAB; /* disable memory */ | |
122 | return (-1); | |
123 | } | |
75334b2a BF |
124 | /* |
125 | * Check for existence of buffers on Unibus. | |
75334b2a | 126 | */ |
39d536e6 | 127 | if (badaddr((caddr_t)ecbuf, 2)) { |
b6220e6c MK |
128 | bad: |
129 | printf("ec%d: buffer mem not found\n", ui->ui_unit); | |
130 | (void) ubamem(uban, ui->ui_flags, ECBUFSIZE*2, 0); | |
a26646de | 131 | addr->ec_rcr = EC_MDISAB; /* disable memory */ |
b6220e6c | 132 | return (-1); |
75334b2a | 133 | } |
a26646de | 134 | #if VAX780 |
b6220e6c MK |
135 | if (cpu == VAX_780 && uba_hd[uban].uh_uba->uba_sr) { |
136 | uba_hd[uban].uh_uba->uba_sr = uba_hd[uban].uh_uba->uba_sr; | |
137 | goto bad; | |
a26646de BF |
138 | } |
139 | #endif | |
b6220e6c MK |
140 | if (ui->ui_alive == 0) /* Only printf from autoconfig */ |
141 | printf("ec%d: mem %x-%x\n", ui->ui_unit, | |
142 | ui->ui_flags, ui->ui_flags + ECBUFSIZE*CLBYTES - 1); | |
143 | ui->ui_type = 1; /* Memory on, allocated */ | |
144 | return (0); | |
145 | } | |
146 | ||
147 | /* | |
148 | * Do output DMA to determine interface presence and | |
149 | * interrupt vector. DMA is too short to disturb other hosts. | |
150 | */ | |
151 | ecprobe(reg, ui) | |
152 | caddr_t reg; | |
153 | struct uba_device *ui; | |
154 | { | |
155 | register int br, cvec; /* r11, r10 value-result */ | |
156 | register struct ecdevice *addr = (struct ecdevice *)reg; | |
157 | register caddr_t ecbuf = (caddr_t) &umem[ui->ui_ubanum][ui->ui_flags]; | |
158 | ||
159 | #ifdef lint | |
160 | br = 0; cvec = br; br = cvec; | |
161 | ecrint(0); ecxint(0); eccollide(0); | |
162 | #endif | |
75334b2a BF |
163 | |
164 | /* | |
b6220e6c | 165 | * Check that buffer memory was found and enabled. |
75334b2a | 166 | */ |
b6220e6c MK |
167 | if (ui->ui_type == 0) |
168 | return(0); | |
75334b2a BF |
169 | /* |
170 | * Make a one byte packet in what should be buffer #0. | |
b6220e6c | 171 | * Submit it for sending. This should cause an xmit interrupt. |
75334b2a BF |
172 | * The xmit interrupt vector is 8 bytes after the receive vector, |
173 | * so adjust for this before returning. | |
174 | */ | |
175 | *(u_short *)ecbuf = (u_short) 03777; | |
176 | ecbuf[03777] = '\0'; | |
177 | addr->ec_xcr = EC_XINTEN|EC_XWBN; | |
178 | DELAY(100000); | |
179 | addr->ec_xcr = EC_XCLR; | |
0ca845ca | 180 | if (cvec > 0 && cvec != 0x200) { |
a26646de BF |
181 | if (cvec & 04) { /* collision interrupt */ |
182 | cvec -= 04; | |
183 | br += 1; /* rcv is collision + 1 */ | |
184 | } else { /* xmit interrupt */ | |
185 | cvec -= 010; | |
186 | br += 2; /* rcv is xmit + 2 */ | |
187 | } | |
0ca845ca | 188 | } |
75334b2a BF |
189 | return (1); |
190 | } | |
191 | ||
192 | /* | |
193 | * Interface exists: make available by filling in network interface | |
194 | * record. System will initialize the interface when it is ready | |
195 | * to accept packets. | |
196 | */ | |
197 | ecattach(ui) | |
198 | struct uba_device *ui; | |
199 | { | |
0ca845ca SL |
200 | struct ec_softc *es = &ec_softc[ui->ui_unit]; |
201 | register struct ifnet *ifp = &es->es_if; | |
75334b2a | 202 | register struct ecdevice *addr = (struct ecdevice *)ui->ui_addr; |
0ca845ca SL |
203 | int i, j; |
204 | u_char *cp; | |
75334b2a | 205 | |
0ca845ca SL |
206 | ifp->if_unit = ui->ui_unit; |
207 | ifp->if_name = "ec"; | |
3fa8d9bb | 208 | ifp->if_mtu = ETHERMTU; |
75334b2a BF |
209 | |
210 | /* | |
0ca845ca | 211 | * Read the ethernet address off the board, one nibble at a time. |
75334b2a | 212 | */ |
667b3286 | 213 | addr->ec_xcr = EC_UECLR; /* zero address pointer */ |
75334b2a | 214 | addr->ec_rcr = EC_AROM; |
7f0e1e06 | 215 | cp = es->es_addr; |
0ca845ca | 216 | #define NEXTBIT addr->ec_rcr = EC_AROM|EC_ASTEP; addr->ec_rcr = EC_AROM |
0217ce84 | 217 | for (i=0; i < sizeof (es->es_addr); i++) { |
75334b2a BF |
218 | *cp = 0; |
219 | for (j=0; j<=4; j+=4) { | |
220 | *cp |= ((addr->ec_rcr >> 8) & 0xf) << j; | |
0ca845ca | 221 | NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT; |
75334b2a BF |
222 | } |
223 | cp++; | |
224 | } | |
87c6a528 MK |
225 | printf("ec%d: hardware address %s\n", ui->ui_unit, |
226 | ether_sprintf(es->es_addr)); | |
0ca845ca | 227 | ifp->if_init = ecinit; |
1cd789fb | 228 | ifp->if_ioctl = ecioctl; |
0ca845ca | 229 | ifp->if_output = ecoutput; |
51595ca2 | 230 | ifp->if_reset = ecreset; |
7f0e1e06 | 231 | ifp->if_flags = IFF_BROADCAST; |
75334b2a | 232 | for (i=0; i<16; i++) |
3f506470 | 233 | es->es_buf[i] |
b6220e6c | 234 | = (u_char *)&umem[ui->ui_ubanum][ui->ui_flags + 2048*i]; |
0ca845ca | 235 | if_attach(ifp); |
75334b2a BF |
236 | } |
237 | ||
238 | /* | |
239 | * Reset of interface after UNIBUS reset. | |
240 | * If interface is on specified uba, reset its state. | |
241 | */ | |
242 | ecreset(unit, uban) | |
243 | int unit, uban; | |
244 | { | |
245 | register struct uba_device *ui; | |
75334b2a BF |
246 | |
247 | if (unit >= NEC || (ui = ecinfo[unit]) == 0 || ui->ui_alive == 0 || | |
248 | ui->ui_ubanum != uban) | |
249 | return; | |
250 | printf(" ec%d", unit); | |
ce3f80e9 | 251 | ec_softc[unit].es_if.if_flags &= ~IFF_RUNNING; |
75334b2a BF |
252 | ecinit(unit); |
253 | } | |
254 | ||
255 | /* | |
256 | * Initialization of interface; clear recorded pending | |
257 | * operations, and reinitialize UNIBUS usage. | |
258 | */ | |
259 | ecinit(unit) | |
260 | int unit; | |
261 | { | |
0ca845ca SL |
262 | struct ec_softc *es = &ec_softc[unit]; |
263 | struct ecdevice *addr; | |
8ae4cebd | 264 | register struct ifnet *ifp = &es->es_if; |
1cd789fb | 265 | int i, s; |
8ae4cebd | 266 | |
7f0e1e06 MK |
267 | /* not yet, if address still unknown */ |
268 | if (ifp->if_addrlist == (struct ifaddr *)0) | |
8ae4cebd | 269 | return; |
75334b2a BF |
270 | |
271 | /* | |
a0d46072 | 272 | * Hang receive buffers and start any pending writes. |
f5a616ae BF |
273 | * Writing into the rcr also makes sure the memory |
274 | * is turned on. | |
75334b2a | 275 | */ |
7f0e1e06 | 276 | if ((ifp->if_flags & IFF_RUNNING) == 0) { |
1cd789fb SL |
277 | addr = (struct ecdevice *)ecinfo[unit]->ui_addr; |
278 | s = splimp(); | |
667b3286 KS |
279 | /* |
280 | * write our ethernet address into the address recognition ROM | |
281 | * so we can always use the same EC_READ bits (referencing ROM), | |
282 | * in case we change the address sometime. | |
0f487295 | 283 | * Note that this is safe here as the receiver is NOT armed. |
667b3286 KS |
284 | */ |
285 | ec_setaddr(es->es_addr, unit); | |
286 | /* | |
0f487295 | 287 | * Arm the receiver |
667b3286 | 288 | */ |
1cd789fb SL |
289 | for (i = ECRHBF; i >= ECRLBF; i--) |
290 | addr->ec_rcr = EC_READ | i; | |
291 | es->es_oactive = 0; | |
292 | es->es_mask = ~0; | |
7f0e1e06 | 293 | es->es_if.if_flags |= IFF_RUNNING; |
1cd789fb SL |
294 | if (es->es_if.if_snd.ifq_head) |
295 | ecstart(unit); | |
296 | splx(s); | |
297 | } | |
75334b2a BF |
298 | } |
299 | ||
75334b2a | 300 | /* |
0f487295 MK |
301 | * Start output on interface. Get another datagram to send |
302 | * off of the interface queue, and copy it to the interface | |
75334b2a BF |
303 | * before starting the output. |
304 | */ | |
b6220e6c | 305 | ecstart(unit) |
75334b2a | 306 | { |
0f487295 | 307 | register struct ec_softc *es = &ec_softc[unit]; |
0ca845ca | 308 | struct ecdevice *addr; |
75334b2a | 309 | struct mbuf *m; |
75334b2a | 310 | |
0f487295 MK |
311 | if ((es->es_if.if_flags & IFF_RUNNING) == 0) |
312 | return; | |
75334b2a | 313 | IF_DEQUEUE(&es->es_if.if_snd, m); |
0f487295 | 314 | if (m == 0) |
75334b2a | 315 | return; |
75334b2a | 316 | ecput(es->es_buf[ECTBF], m); |
0ca845ca | 317 | addr = (struct ecdevice *)ecinfo[unit]->ui_addr; |
75334b2a BF |
318 | addr->ec_xcr = EC_WRITE|ECTBF; |
319 | es->es_oactive = 1; | |
320 | } | |
321 | ||
322 | /* | |
323 | * Ethernet interface transmitter interrupt. | |
324 | * Start another output if more data to send. | |
325 | */ | |
326 | ecxint(unit) | |
327 | int unit; | |
328 | { | |
75334b2a | 329 | register struct ec_softc *es = &ec_softc[unit]; |
0ca845ca SL |
330 | register struct ecdevice *addr = |
331 | (struct ecdevice *)ecinfo[unit]->ui_addr; | |
75334b2a BF |
332 | |
333 | if (es->es_oactive == 0) | |
334 | return; | |
0ca845ca SL |
335 | if ((addr->ec_xcr&EC_XDONE) == 0 || (addr->ec_xcr&EC_XBN) != ECTBF) { |
336 | printf("ec%d: stray xmit interrupt, xcr=%b\n", unit, | |
337 | addr->ec_xcr, EC_XBITS); | |
338 | es->es_oactive = 0; | |
339 | addr->ec_xcr = EC_XCLR; | |
340 | return; | |
341 | } | |
75334b2a BF |
342 | es->es_if.if_opackets++; |
343 | es->es_oactive = 0; | |
75334b2a BF |
344 | es->es_mask = ~0; |
345 | addr->ec_xcr = EC_XCLR; | |
0ca845ca SL |
346 | if (es->es_if.if_snd.ifq_head) |
347 | ecstart(unit); | |
75334b2a BF |
348 | } |
349 | ||
350 | /* | |
351 | * Collision on ethernet interface. Do exponential | |
352 | * backoff, and retransmit. If have backed off all | |
353 | * the way print warning diagnostic, and drop packet. | |
354 | */ | |
355 | eccollide(unit) | |
356 | int unit; | |
75334b2a BF |
357 | { |
358 | register struct ec_softc *es = &ec_softc[unit]; | |
be515f6e BF |
359 | register struct ecdevice *addr = |
360 | (struct ecdevice *)ecinfo[unit]->ui_addr; | |
361 | register i; | |
362 | int delay; | |
75334b2a | 363 | |
0f487295 MK |
364 | es->es_if.if_collisions++; |
365 | if (es->es_oactive == 0) | |
366 | return; | |
367 | ||
75334b2a BF |
368 | /* |
369 | * Es_mask is a 16 bit number with n low zero bits, with | |
370 | * n the number of backoffs. When es_mask is 0 we have | |
371 | * backed off 16 times, and give up. | |
372 | */ | |
373 | if (es->es_mask == 0) { | |
be515f6e | 374 | es->es_if.if_oerrors++; |
0f487295 | 375 | log(LOG_ERR, "ec%d: send error\n", unit); |
75334b2a | 376 | /* |
be515f6e BF |
377 | * Reset interface, then requeue rcv buffers. |
378 | * Some incoming packets may be lost, but that | |
379 | * can't be helped. | |
380 | */ | |
381 | addr->ec_xcr = EC_UECLR; | |
382 | for (i=ECRHBF; i>=ECRLBF; i--) | |
383 | addr->ec_rcr = EC_READ|i; | |
384 | /* | |
385 | * Reset and transmit next packet (if any). | |
75334b2a | 386 | */ |
be515f6e BF |
387 | es->es_oactive = 0; |
388 | es->es_mask = ~0; | |
389 | if (es->es_if.if_snd.ifq_head) | |
390 | ecstart(unit); | |
75334b2a BF |
391 | return; |
392 | } | |
393 | /* | |
be515f6e | 394 | * Do exponential backoff. Compute delay based on low bits |
0f487295 MK |
395 | * of the interval timer (1 bit for each transmission attempt, |
396 | * but at most 5 bits). Then delay for that number of | |
be515f6e BF |
397 | * slot times. A slot time is 51.2 microseconds (rounded to 51). |
398 | * This does not take into account the time already used to | |
399 | * process the interrupt. | |
75334b2a BF |
400 | */ |
401 | es->es_mask <<= 1; | |
0f487295 | 402 | delay = mfpr(ICR) & 0x1f &~ es->es_mask; |
be515f6e | 403 | DELAY(delay * 51); |
75334b2a | 404 | /* |
be515f6e | 405 | * Clear the controller's collision flag, thus enabling retransmit. |
75334b2a | 406 | */ |
a26646de | 407 | addr->ec_xcr = EC_CLEAR; |
75334b2a BF |
408 | } |
409 | ||
75334b2a BF |
410 | /* |
411 | * Ethernet interface receiver interrupt. | |
412 | * If input error just drop packet. | |
667b3286 | 413 | * Otherwise examine |
75334b2a BF |
414 | * packet to determine type. If can't determine length |
415 | * from type, then have to drop packet. Othewise decapsulate | |
416 | * packet based on type and pass to type specific higher-level | |
417 | * input routine. | |
418 | */ | |
419 | ecrint(unit) | |
420 | int unit; | |
421 | { | |
422 | struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr; | |
75334b2a | 423 | |
75334b2a BF |
424 | while (addr->ec_rcr & EC_RDONE) |
425 | ecread(unit); | |
426 | } | |
427 | ||
428 | ecread(unit) | |
429 | int unit; | |
430 | { | |
431 | register struct ec_softc *es = &ec_softc[unit]; | |
432 | struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr; | |
3fa8d9bb | 433 | register struct ether_header *ec; |
75334b2a | 434 | struct mbuf *m; |
39d536e6 | 435 | int len, off, resid, ecoff, rbuf; |
75334b2a | 436 | register struct ifqueue *inq; |
39d536e6 | 437 | u_char *ecbuf; |
75334b2a BF |
438 | |
439 | es->es_if.if_ipackets++; | |
39d536e6 BJ |
440 | rbuf = addr->ec_rcr & EC_RBN; |
441 | if (rbuf < ECRLBF || rbuf > ECRHBF) | |
75334b2a | 442 | panic("ecrint"); |
39d536e6 | 443 | ecbuf = es->es_buf[rbuf]; |
75334b2a | 444 | ecoff = *(short *)ecbuf; |
be515f6e | 445 | if (ecoff <= ECRDOFF || ecoff > 2046) { |
75334b2a BF |
446 | es->es_if.if_ierrors++; |
447 | #ifdef notdef | |
448 | if (es->es_if.if_ierrors % 100 == 0) | |
449 | printf("ec%d: += 100 input errors\n", unit); | |
450 | #endif | |
75334b2a BF |
451 | goto setup; |
452 | } | |
453 | ||
454 | /* | |
455 | * Get input data length. | |
456 | * Get pointer to ethernet header (in input buffer). | |
7f0e1e06 | 457 | * Deal with trailer protocol: if type is trailer type |
75334b2a BF |
458 | * get true type from first 16-bit word past data. |
459 | * Remember that type was trailer by setting off. | |
460 | */ | |
3fa8d9bb SL |
461 | len = ecoff - ECRDOFF - sizeof (struct ether_header); |
462 | ec = (struct ether_header *)(ecbuf + ECRDOFF); | |
463 | ec->ether_type = ntohs((u_short)ec->ether_type); | |
75334b2a | 464 | #define ecdataaddr(ec, off, type) ((type)(((caddr_t)((ec)+1)+(off)))) |
7f0e1e06 MK |
465 | if (ec->ether_type >= ETHERTYPE_TRAIL && |
466 | ec->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
467 | off = (ec->ether_type - ETHERTYPE_TRAIL) * 512; | |
3fa8d9bb | 468 | if (off >= ETHERMTU) |
75334b2a | 469 | goto setup; /* sanity */ |
3fa8d9bb SL |
470 | ec->ether_type = ntohs(*ecdataaddr(ec, off, u_short *)); |
471 | resid = ntohs(*(ecdataaddr(ec, off+2, u_short *))); | |
75334b2a BF |
472 | if (off + resid > len) |
473 | goto setup; /* sanity */ | |
474 | len = off + resid; | |
475 | } else | |
476 | off = 0; | |
477 | if (len == 0) | |
478 | goto setup; | |
479 | ||
480 | /* | |
481 | * Pull packet off interface. Off is nonzero if packet | |
482 | * has trailing header; ecget will then force this header | |
483 | * information to be at the front, but we still have to drop | |
484 | * the type and length which are at the front of any trailer data. | |
485 | */ | |
2cf743f4 | 486 | m = ecget(ecbuf, len, off, &es->es_if); |
75334b2a BF |
487 | if (m == 0) |
488 | goto setup; | |
489 | if (off) { | |
2cf743f4 MK |
490 | struct ifnet *ifp; |
491 | ||
492 | ifp = *(mtod(m, struct ifnet **)); | |
75334b2a BF |
493 | m->m_off += 2 * sizeof (u_short); |
494 | m->m_len -= 2 * sizeof (u_short); | |
2cf743f4 | 495 | *(mtod(m, struct ifnet **)) = ifp; |
75334b2a | 496 | } |
3fa8d9bb | 497 | switch (ec->ether_type) { |
75334b2a BF |
498 | |
499 | #ifdef INET | |
7f0e1e06 | 500 | case ETHERTYPE_IP: |
75334b2a BF |
501 | schednetisr(NETISR_IP); |
502 | inq = &ipintrq; | |
503 | break; | |
8ae4cebd | 504 | |
7f0e1e06 | 505 | case ETHERTYPE_ARP: |
8ae4cebd | 506 | arpinput(&es->es_ac, m); |
27bcf34c | 507 | goto setup; |
cd4baa58 KS |
508 | #endif |
509 | #ifdef NS | |
510 | case ETHERTYPE_NS: | |
511 | schednetisr(NETISR_NS); | |
512 | inq = &nsintrq; | |
513 | break; | |
514 | ||
75334b2a BF |
515 | #endif |
516 | default: | |
517 | m_freem(m); | |
518 | goto setup; | |
519 | } | |
520 | ||
521 | if (IF_QFULL(inq)) { | |
522 | IF_DROP(inq); | |
523 | m_freem(m); | |
0ca845ca SL |
524 | goto setup; |
525 | } | |
526 | IF_ENQUEUE(inq, m); | |
75334b2a BF |
527 | |
528 | setup: | |
529 | /* | |
530 | * Reset for next packet. | |
531 | */ | |
39d536e6 | 532 | addr->ec_rcr = EC_READ|EC_RCLR|rbuf; |
75334b2a BF |
533 | } |
534 | ||
535 | /* | |
536 | * Ethernet output routine. | |
537 | * Encapsulate a packet of type family for the local net. | |
538 | * Use trailer local net encapsulation if enough data in first | |
539 | * packet leaves a multiple of 512 bytes of data in remainder. | |
be515f6e BF |
540 | * If destination is this address or broadcast, send packet to |
541 | * loop device to kludge around the fact that 3com interfaces can't | |
542 | * talk to themselves. | |
75334b2a BF |
543 | */ |
544 | ecoutput(ifp, m0, dst) | |
545 | struct ifnet *ifp; | |
546 | struct mbuf *m0; | |
547 | struct sockaddr *dst; | |
548 | { | |
8ae4cebd | 549 | int type, s, error; |
7f0e1e06 | 550 | u_char edst[6]; |
8ae4cebd | 551 | struct in_addr idst; |
75334b2a BF |
552 | register struct ec_softc *es = &ec_softc[ifp->if_unit]; |
553 | register struct mbuf *m = m0; | |
3fa8d9bb | 554 | register struct ether_header *ec; |
66923854 | 555 | register int off; |
8ae4cebd | 556 | struct mbuf *mcopy = (struct mbuf *)0; |
87c6a528 | 557 | int usetrailers; |
75334b2a | 558 | |
0f487295 MK |
559 | if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { |
560 | error = ENETDOWN; | |
561 | goto bad; | |
562 | } | |
75334b2a BF |
563 | switch (dst->sa_family) { |
564 | ||
565 | #ifdef INET | |
566 | case AF_INET: | |
8ae4cebd | 567 | idst = ((struct sockaddr_in *)dst)->sin_addr; |
87c6a528 | 568 | if (!arpresolve(&es->es_ac, m, &idst, edst, &usetrailers)) |
8ae4cebd | 569 | return (0); /* if not yet resolved */ |
7f0e1e06 MK |
570 | if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, |
571 | sizeof(edst))) | |
92aca3da | 572 | mcopy = m_copy(m, 0, (int)M_COPYALL); |
75334b2a | 573 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; |
1cd789fb | 574 | /* need per host negotiation */ |
87c6a528 | 575 | if (usetrailers && off > 0 && (off & 0x1ff) == 0 && |
75334b2a | 576 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { |
7f0e1e06 | 577 | type = ETHERTYPE_TRAIL + (off>>9); |
75334b2a BF |
578 | m->m_off -= 2 * sizeof (u_short); |
579 | m->m_len += 2 * sizeof (u_short); | |
7f0e1e06 | 580 | *mtod(m, u_short *) = ntohs((u_short)ETHERTYPE_IP); |
3fa8d9bb | 581 | *(mtod(m, u_short *) + 1) = ntohs((u_short)m->m_len); |
75334b2a BF |
582 | goto gottrailertype; |
583 | } | |
7f0e1e06 | 584 | type = ETHERTYPE_IP; |
75334b2a BF |
585 | off = 0; |
586 | goto gottype; | |
75334b2a | 587 | #endif |
cd4baa58 KS |
588 | #ifdef NS |
589 | case AF_NS: | |
590 | bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), | |
591 | (caddr_t)edst, sizeof (edst)); | |
592 | ||
593 | if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost, | |
594 | sizeof(edst))) { | |
595 | ||
596 | mcopy = m_copy(m, 0, (int)M_COPYALL); | |
597 | } else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, | |
598 | sizeof(edst))) { | |
599 | ||
600 | return(looutput(&loif, m, dst)); | |
601 | } | |
602 | type = ETHERTYPE_NS; | |
603 | off = 0; | |
604 | goto gottype; | |
605 | #endif | |
75334b2a | 606 | |
8ae4cebd SL |
607 | case AF_UNSPEC: |
608 | ec = (struct ether_header *)dst->sa_data; | |
7f0e1e06 | 609 | bcopy((caddr_t)ec->ether_dhost, (caddr_t)edst, sizeof (edst)); |
8ae4cebd SL |
610 | type = ec->ether_type; |
611 | goto gottype; | |
612 | ||
75334b2a BF |
613 | default: |
614 | printf("ec%d: can't handle af%d\n", ifp->if_unit, | |
615 | dst->sa_family); | |
616 | error = EAFNOSUPPORT; | |
617 | goto bad; | |
618 | } | |
619 | ||
620 | gottrailertype: | |
621 | /* | |
622 | * Packet to be sent as trailer: move first packet | |
623 | * (control information) to end of chain. | |
624 | */ | |
625 | while (m->m_next) | |
626 | m = m->m_next; | |
627 | m->m_next = m0; | |
628 | m = m0->m_next; | |
629 | m0->m_next = 0; | |
630 | m0 = m; | |
631 | ||
632 | gottype: | |
633 | /* | |
634 | * Add local net header. If no space in first mbuf, | |
635 | * allocate another. | |
636 | */ | |
637 | if (m->m_off > MMAXOFF || | |
3fa8d9bb | 638 | MMINOFF + sizeof (struct ether_header) > m->m_off) { |
cce93e4b | 639 | m = m_get(M_DONTWAIT, MT_HEADER); |
75334b2a BF |
640 | if (m == 0) { |
641 | error = ENOBUFS; | |
642 | goto bad; | |
643 | } | |
644 | m->m_next = m0; | |
645 | m->m_off = MMINOFF; | |
3fa8d9bb | 646 | m->m_len = sizeof (struct ether_header); |
75334b2a | 647 | } else { |
3fa8d9bb SL |
648 | m->m_off -= sizeof (struct ether_header); |
649 | m->m_len += sizeof (struct ether_header); | |
75334b2a | 650 | } |
3fa8d9bb | 651 | ec = mtod(m, struct ether_header *); |
7f0e1e06 MK |
652 | bcopy((caddr_t)edst, (caddr_t)ec->ether_dhost, sizeof (edst)); |
653 | bcopy((caddr_t)es->es_addr, (caddr_t)ec->ether_shost, | |
654 | sizeof(ec->ether_shost)); | |
3fa8d9bb | 655 | ec->ether_type = htons((u_short)type); |
75334b2a BF |
656 | |
657 | /* | |
658 | * Queue message on interface, and start output if interface | |
659 | * not yet active. | |
660 | */ | |
661 | s = splimp(); | |
662 | if (IF_QFULL(&ifp->if_snd)) { | |
663 | IF_DROP(&ifp->if_snd); | |
664 | error = ENOBUFS; | |
665 | goto qfull; | |
666 | } | |
667 | IF_ENQUEUE(&ifp->if_snd, m); | |
668 | if (es->es_oactive == 0) | |
669 | ecstart(ifp->if_unit); | |
670 | splx(s); | |
66923854 | 671 | return (mcopy ? looutput(&loif, mcopy, dst) : 0); |
0ca845ca | 672 | |
75334b2a BF |
673 | qfull: |
674 | m0 = m; | |
675 | splx(s); | |
676 | bad: | |
677 | m_freem(m0); | |
ce3f80e9 MK |
678 | if (mcopy) |
679 | m_freem(mcopy); | |
66923854 | 680 | return (error); |
75334b2a BF |
681 | } |
682 | ||
683 | /* | |
4fce3bf9 | 684 | * Routine to copy from mbuf chain to transmit |
0ca845ca | 685 | * buffer in UNIBUS memory. |
4fce3bf9 SL |
686 | * If packet size is less than the minimum legal size, |
687 | * the buffer is expanded. We probably should zero out the extra | |
688 | * bytes for security, but that would slow things down. | |
75334b2a BF |
689 | */ |
690 | ecput(ecbuf, m) | |
0ca845ca | 691 | u_char *ecbuf; |
75334b2a BF |
692 | struct mbuf *m; |
693 | { | |
75334b2a | 694 | register struct mbuf *mp; |
0ca845ca | 695 | register int off; |
48db90de | 696 | u_char *bp; |
75334b2a | 697 | |
0ca845ca SL |
698 | for (off = 2048, mp = m; mp; mp = mp->m_next) |
699 | off -= mp->m_len; | |
3fa8d9bb SL |
700 | if (2048 - off < ETHERMIN + sizeof (struct ether_header)) |
701 | off = 2048 - ETHERMIN - sizeof (struct ether_header); | |
0ca845ca SL |
702 | *(u_short *)ecbuf = off; |
703 | bp = (u_char *)(ecbuf + off); | |
48db90de SL |
704 | for (mp = m; mp; mp = mp->m_next) { |
705 | register unsigned len = mp->m_len; | |
706 | u_char *mcp; | |
0ca845ca | 707 | |
0ca845ca SL |
708 | if (len == 0) |
709 | continue; | |
710 | mcp = mtod(mp, u_char *); | |
711 | if ((unsigned)bp & 01) { | |
4a404244 | 712 | *bp++ = *mcp++; |
0ca845ca | 713 | len--; |
4a404244 | 714 | } |
48db90de SL |
715 | if (off = (len >> 1)) { |
716 | register u_short *to, *from; | |
717 | ||
718 | to = (u_short *)bp; | |
719 | from = (u_short *)mcp; | |
720 | do | |
721 | *to++ = *from++; | |
722 | while (--off > 0); | |
723 | bp = (u_char *)to, | |
724 | mcp = (u_char *)from; | |
4a404244 | 725 | } |
48db90de | 726 | if (len & 01) |
75334b2a | 727 | *bp++ = *mcp++; |
75334b2a | 728 | } |
48db90de | 729 | m_freem(m); |
75334b2a BF |
730 | } |
731 | ||
732 | /* | |
733 | * Routine to copy from UNIBUS memory into mbufs. | |
734 | * Similar in spirit to if_rubaget. | |
4a404244 BJ |
735 | * |
736 | * Warning: This makes the fairly safe assumption that | |
737 | * mbufs have even lengths. | |
75334b2a BF |
738 | */ |
739 | struct mbuf * | |
2cf743f4 | 740 | ecget(ecbuf, totlen, off0, ifp) |
48db90de | 741 | u_char *ecbuf; |
75334b2a | 742 | int totlen, off0; |
2cf743f4 | 743 | struct ifnet *ifp; |
75334b2a | 744 | { |
48db90de SL |
745 | register struct mbuf *m; |
746 | struct mbuf *top = 0, **mp = ⊤ | |
747 | register int off = off0, len; | |
748 | u_char *cp; | |
75334b2a | 749 | |
3fa8d9bb | 750 | cp = ecbuf + ECRDOFF + sizeof (struct ether_header); |
75334b2a | 751 | while (totlen > 0) { |
48db90de SL |
752 | register int words; |
753 | u_char *mcp; | |
754 | ||
cce93e4b | 755 | MGET(m, M_DONTWAIT, MT_DATA); |
75334b2a BF |
756 | if (m == 0) |
757 | goto bad; | |
758 | if (off) { | |
759 | len = totlen - off; | |
3fa8d9bb SL |
760 | cp = ecbuf + ECRDOFF + |
761 | sizeof (struct ether_header) + off; | |
75334b2a BF |
762 | } else |
763 | len = totlen; | |
2cf743f4 MK |
764 | if (ifp) |
765 | len += sizeof(ifp); | |
766 | if (len >= NBPG) { | |
658e19b1 MK |
767 | MCLGET(m); |
768 | if (m->m_len == CLBYTES) | |
2cf743f4 | 769 | m->m_len = len = MIN(len, CLBYTES); |
658e19b1 | 770 | else |
75334b2a | 771 | m->m_len = len = MIN(MLEN, len); |
75334b2a BF |
772 | } else { |
773 | m->m_len = len = MIN(MLEN, len); | |
774 | m->m_off = MMINOFF; | |
775 | } | |
48db90de | 776 | mcp = mtod(m, u_char *); |
2cf743f4 MK |
777 | if (ifp) { |
778 | /* | |
779 | * Prepend interface pointer to first mbuf. | |
780 | */ | |
781 | *(mtod(m, struct ifnet **)) = ifp; | |
782 | mcp += sizeof(ifp); | |
783 | len -= sizeof(ifp); | |
784 | ifp = (struct ifnet *)0; | |
785 | } | |
48db90de SL |
786 | if (words = (len >> 1)) { |
787 | register u_short *to, *from; | |
788 | ||
789 | to = (u_short *)mcp; | |
790 | from = (u_short *)cp; | |
791 | do | |
792 | *to++ = *from++; | |
793 | while (--words > 0); | |
794 | mcp = (u_char *)to; | |
795 | cp = (u_char *)from; | |
4a404244 | 796 | } |
0ca845ca | 797 | if (len & 01) |
75334b2a BF |
798 | *mcp++ = *cp++; |
799 | *mp = m; | |
800 | mp = &m->m_next; | |
48db90de | 801 | if (off == 0) { |
75334b2a | 802 | totlen -= len; |
48db90de SL |
803 | continue; |
804 | } | |
805 | off += len; | |
806 | if (off == totlen) { | |
3fa8d9bb | 807 | cp = ecbuf + ECRDOFF + sizeof (struct ether_header); |
48db90de SL |
808 | off = 0; |
809 | totlen = off0; | |
810 | } | |
75334b2a BF |
811 | } |
812 | return (top); | |
813 | bad: | |
814 | m_freem(top); | |
815 | return (0); | |
816 | } | |
1cd789fb SL |
817 | |
818 | /* | |
819 | * Process an ioctl request. | |
820 | */ | |
821 | ecioctl(ifp, cmd, data) | |
822 | register struct ifnet *ifp; | |
823 | int cmd; | |
824 | caddr_t data; | |
825 | { | |
7f0e1e06 | 826 | register struct ifaddr *ifa = (struct ifaddr *)data; |
0f487295 MK |
827 | struct ec_softc *es = &ec_softc[ifp->if_unit]; |
828 | struct ecdevice *addr; | |
1cd789fb SL |
829 | int s = splimp(), error = 0; |
830 | ||
0f487295 MK |
831 | addr = (struct ecdevice *)(ecinfo[ifp->if_unit]->ui_addr); |
832 | ||
1cd789fb SL |
833 | switch (cmd) { |
834 | ||
835 | case SIOCSIFADDR: | |
7f0e1e06 | 836 | ifp->if_flags |= IFF_UP; |
7f0e1e06 MK |
837 | |
838 | switch (ifa->ifa_addr.sa_family) { | |
cd4baa58 | 839 | #ifdef INET |
7f0e1e06 | 840 | case AF_INET: |
0f487295 | 841 | ecinit(ifp->if_unit); /* before arpwhohas */ |
7f0e1e06 MK |
842 | ((struct arpcom *)ifp)->ac_ipaddr = |
843 | IA_SIN(ifa)->sin_addr; | |
844 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
845 | break; | |
cd4baa58 KS |
846 | #endif |
847 | #ifdef NS | |
848 | case AF_NS: | |
849 | { | |
850 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
851 | ||
0f487295 MK |
852 | if (ns_nullhost(*ina)) |
853 | ina->x_host = *(union ns_host *)(es->es_addr); | |
854 | else { | |
667b3286 KS |
855 | /* |
856 | * The manual says we can't change the address | |
0f487295 MK |
857 | * while the receiver is armed, |
858 | * so reset everything | |
667b3286 | 859 | */ |
0f487295 | 860 | ifp->if_flags &= ~IFF_RUNNING; |
8011f5df MK |
861 | bcopy((caddr_t)ina->x_host.c_host, |
862 | (caddr_t)es->es_addr, sizeof(es->es_addr)); | |
cd4baa58 | 863 | } |
667b3286 | 864 | ecinit(ifp->if_unit); /* does ec_setaddr() */ |
cd4baa58 KS |
865 | break; |
866 | } | |
867 | #endif | |
0f487295 MK |
868 | default: |
869 | ecinit(ifp->if_unit); | |
870 | break; | |
7f0e1e06 | 871 | } |
1cd789fb SL |
872 | break; |
873 | ||
0f487295 MK |
874 | case SIOCSIFFLAGS: |
875 | if ((ifp->if_flags & IFF_UP) == 0 && | |
876 | ifp->if_flags & IFF_RUNNING) { | |
877 | addr->ec_xcr = EC_UECLR; | |
878 | ifp->if_flags &= ~IFF_RUNNING; | |
879 | } else if (ifp->if_flags & IFF_UP && | |
880 | (ifp->if_flags & IFF_RUNNING) == 0) | |
881 | ecinit(ifp->if_unit); | |
882 | break; | |
883 | ||
1cd789fb SL |
884 | default: |
885 | error = EINVAL; | |
886 | } | |
887 | splx(s); | |
888 | return (error); | |
889 | } | |
cd4baa58 KS |
890 | |
891 | ec_setaddr(physaddr,unit) | |
0f487295 MK |
892 | u_char *physaddr; |
893 | int unit; | |
cd4baa58 KS |
894 | { |
895 | struct ec_softc *es = &ec_softc[unit]; | |
896 | struct uba_device *ui = ecinfo[unit]; | |
897 | register struct ecdevice *addr = (struct ecdevice *)ui->ui_addr; | |
898 | register char nibble; | |
899 | register int i, j; | |
2cf743f4 | 900 | |
cd4baa58 KS |
901 | /* |
902 | * Use the ethernet address supplied | |
0f487295 | 903 | * Note that we do a UECLR here, so the receive buffers |
667b3286 | 904 | * must be requeued. |
cd4baa58 KS |
905 | */ |
906 | ||
667b3286 | 907 | #ifdef DEBUG |
87c6a528 MK |
908 | printf("ec_setaddr: setting address for unit %d = %s", |
909 | unit, ether_sprintf(physaddr)); | |
667b3286 KS |
910 | #endif |
911 | addr->ec_xcr = EC_UECLR; | |
cd4baa58 | 912 | addr->ec_rcr = 0; |
667b3286 | 913 | /* load requested address */ |
cd4baa58 KS |
914 | for (i = 0; i < 6; i++) { /* 6 bytes of address */ |
915 | es->es_addr[i] = physaddr[i]; | |
916 | nibble = physaddr[i] & 0xf; /* lower nibble */ | |
917 | addr->ec_rcr = (nibble << 8); | |
667b3286 | 918 | addr->ec_rcr = (nibble << 8) + EC_AWCLK; /* latch nibble */ |
cd4baa58 KS |
919 | addr->ec_rcr = (nibble << 8); |
920 | for (j=0; j < 4; j++) { | |
921 | addr->ec_rcr = 0; | |
922 | addr->ec_rcr = EC_ASTEP; /* step counter */ | |
923 | addr->ec_rcr = 0; | |
924 | } | |
925 | nibble = (physaddr[i] >> 4) & 0xf; /* upper nibble */ | |
926 | addr->ec_rcr = (nibble << 8); | |
667b3286 | 927 | addr->ec_rcr = (nibble << 8) + EC_AWCLK; /* latch nibble */ |
cd4baa58 KS |
928 | addr->ec_rcr = (nibble << 8); |
929 | for (j=0; j < 4; j++) { | |
930 | addr->ec_rcr = 0; | |
931 | addr->ec_rcr = EC_ASTEP; /* step counter */ | |
932 | addr->ec_rcr = 0; | |
933 | } | |
934 | } | |
667b3286 KS |
935 | #ifdef DEBUG |
936 | /* | |
937 | * Read the ethernet address off the board, one nibble at a time. | |
938 | */ | |
939 | addr->ec_xcr = EC_UECLR; | |
940 | addr->ec_rcr = 0; /* read RAM */ | |
941 | cp = es->es_addr; | |
942 | #undef NEXTBIT | |
943 | #define NEXTBIT addr->ec_rcr = EC_ASTEP; addr->ec_rcr = 0 | |
944 | for (i=0; i < sizeof (es->es_addr); i++) { | |
945 | *cp = 0; | |
946 | for (j=0; j<=4; j+=4) { | |
947 | *cp |= ((addr->ec_rcr >> 8) & 0xf) << j; | |
948 | NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT; | |
949 | } | |
950 | cp++; | |
951 | } | |
87c6a528 MK |
952 | printf("ec_setaddr: RAM address for unit %d = %s", |
953 | unit, ether_sprintf(physaddr)); | |
667b3286 | 954 | #endif |
cd4baa58 | 955 | } |
9a0b0c74 | 956 | #endif |