Commit | Line | Data |
---|---|---|
430f81c3 MK |
1 | /* |
2 | * Copyright (c) 1988 Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
003ca8a0 MK |
5 | * This code is derived from software contributed to Berkeley by |
6 | * Computer Consoles Inc. | |
7 | * | |
430f81c3 | 8 | * Redistribution and use in source and binary forms are permitted |
616d42db KB |
9 | * provided that the above copyright notice and this paragraph are |
10 | * duplicated in all such forms and that any documentation, | |
11 | * advertising materials, and other materials related to such | |
12 | * distribution and use acknowledge that the software was developed | |
13 | * by the University of California, Berkeley. The name of the | |
14 | * University may not be used to endorse or promote products derived | |
15 | * from this software without specific prior written permission. | |
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
17 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
18 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
430f81c3 | 19 | * |
c18e27ed | 20 | * @(#)if_ace.c 7.5 (Berkeley) %G% |
430f81c3 | 21 | */ |
bc54ef3d SL |
22 | |
23 | /* | |
24 | * ACC VERSAbus Ethernet controller | |
25 | */ | |
26 | #include "ace.h" | |
27 | #if NACE > 0 | |
28 | ||
4a6cc6f7 SL |
29 | #include "param.h" |
30 | #include "systm.h" | |
003ca8a0 | 31 | #include "malloc.h" |
4a6cc6f7 SL |
32 | #include "mbuf.h" |
33 | #include "buf.h" | |
34 | #include "protosw.h" | |
35 | #include "socket.h" | |
36 | #include "vmmac.h" | |
37 | #include "ioctl.h" | |
38 | #include "errno.h" | |
39 | #include "vmparam.h" | |
56ad8000 | 40 | #include "syslog.h" |
bc54ef3d SL |
41 | |
42 | #include "../net/if.h" | |
43 | #include "../net/netisr.h" | |
44 | #include "../net/route.h" | |
56ad8000 | 45 | #ifdef INET |
bc54ef3d SL |
46 | #include "../netinet/in.h" |
47 | #include "../netinet/in_systm.h" | |
4a6cc6f7 | 48 | #include "../netinet/in_var.h" |
bc54ef3d SL |
49 | #include "../netinet/ip.h" |
50 | #include "../netinet/ip_var.h" | |
51 | #include "../netinet/if_ether.h" | |
56ad8000 SL |
52 | #endif |
53 | #ifdef NS | |
54 | #include "../netns/ns.h" | |
55 | #include "../netns/ns_if.h" | |
56 | #endif | |
bc54ef3d | 57 | |
c18e27ed KM |
58 | #include "machine/cpu.h" |
59 | #include "machine/pte.h" | |
cee84cde | 60 | |
bc54ef3d SL |
61 | #include "../tahoe/mtpr.h" |
62 | #include "../tahoeif/if_acereg.h" | |
4a6cc6f7 | 63 | #include "../tahoevba/vbavar.h" |
bc54ef3d | 64 | |
003ca8a0 | 65 | int aceprobe(), aceattach(), acerint(), acecint(), acestart(); |
bc54ef3d | 66 | struct vba_device *aceinfo[NACE]; |
5efb6689 | 67 | long acestd[] = { 0 }; |
bc54ef3d | 68 | struct vba_driver acedriver = |
0d5152c1 | 69 | { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 }; |
bc54ef3d SL |
70 | |
71 | int aceinit(), aceoutput(), aceioctl(), acereset(); | |
72 | struct mbuf *aceget(); | |
73 | ||
74 | /* | |
75 | * Ethernet software status per interface. | |
76 | * | |
77 | * Each interface is referenced by a network interface structure, | |
78 | * is_if, which the routing code uses to locate the interface. | |
79 | * This structure contains the output queue for the interface, its address, ... | |
80 | */ | |
81 | struct ace_softc { | |
82 | struct arpcom is_ac; /* Ethernet common part */ | |
83 | #define is_if is_ac.ac_if /* network-visible interface */ | |
84 | #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ | |
bc54ef3d SL |
85 | short is_flags; |
86 | #define ACEF_OACTIVE 0x1 /* output is active */ | |
87 | #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */ | |
88 | short is_promiscuous; /* true is enabled */ | |
89 | short is_segboundry; /* first TX Seg in dpm */ | |
90 | short is_eictr; /* Rx segment tracking ctr */ | |
91 | short is_eoctr; /* Tx segment tracking ctr */ | |
92 | short is_txnext; /* Next available Tx segment */ | |
93 | short is_currnd; /* current random backoff */ | |
94 | struct ace_stats is_stats; /* holds board statistics */ | |
95 | short is_xcnt; /* count xmitted segments to be acked | |
96 | by the controller */ | |
56ad8000 | 97 | long is_ivec; /* autoconfig interrupt vector base */ |
0d5152c1 SL |
98 | struct pte *is_map; /* pte map for dual ported memory */ |
99 | caddr_t is_dpm; /* address of mapped memory */ | |
bc54ef3d SL |
100 | } ace_softc[NACE]; |
101 | extern struct ifnet loif; | |
102 | ||
56ad8000 | 103 | aceprobe(reg, vi) |
bc54ef3d | 104 | caddr_t reg; |
56ad8000 | 105 | struct vba_device *vi; |
bc54ef3d | 106 | { |
56ad8000 SL |
107 | register br, cvec; /* must be r12, r11 */ |
108 | struct acedevice *ap = (struct acedevice *)reg; | |
109 | struct ace_softc *is = &ace_softc[vi->ui_unit]; | |
bc54ef3d SL |
110 | |
111 | #ifdef lint | |
9d61b7ff | 112 | br = 0; cvec = br; br = cvec; |
bc54ef3d SL |
113 | acerint(0); acecint(0); |
114 | #endif | |
115 | if (badaddr(reg, 2)) | |
56ad8000 SL |
116 | return (0); |
117 | movow(&ap->csr, CSR_RESET); | |
bc54ef3d | 118 | DELAY(10000); |
56ad8000 SL |
119 | #ifdef notdef |
120 | /* | |
121 | * Select two spaces for the interrupts aligned to an | |
122 | * eight vector boundary and fitting in 8 bits (as | |
123 | * required by the controller) -- YECH. The controller | |
124 | * will be notified later at initialization time. | |
125 | */ | |
126 | if ((vi->ui_hd->vh_lastiv -= 2) > 0xff) | |
127 | vi->ui_hd->vh_lastiv = 0x200; | |
128 | is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7; | |
129 | #else | |
130 | is->is_ivec = 0x90+vi->ui_unit*8; | |
131 | #endif | |
132 | br = 0x14, cvec = is->is_ivec; /* XXX */ | |
133 | return (sizeof (*ap)); | |
bc54ef3d SL |
134 | } |
135 | ||
136 | /* | |
137 | * Interface exists: make available by filling in network interface | |
138 | * record. System will initialize the interface when it is ready | |
139 | * to accept packets. | |
140 | */ | |
141 | aceattach(ui) | |
142 | struct vba_device *ui; | |
143 | { | |
144 | register short unit = ui->ui_unit; | |
145 | register struct ace_softc *is = &ace_softc[unit]; | |
146 | register struct ifnet *ifp = &is->is_if; | |
147 | register struct acedevice *addr = (struct acedevice *)ui->ui_addr; | |
148 | register short *wp, i; | |
bc54ef3d SL |
149 | |
150 | ifp->if_unit = unit; | |
151 | ifp->if_name = "ace"; | |
152 | ifp->if_mtu = ETHERMTU; | |
153 | /* | |
b50833d9 | 154 | * Get station's addresses and set multicast hash table. |
bc54ef3d | 155 | */ |
b50833d9 SL |
156 | for (wp = (short *)addr->station, i = 0; i < 6; i++) |
157 | is->is_addr[i] = ~*wp++; | |
158 | printf("ace%d: hardware address %s\n", unit, | |
159 | ether_sprintf(is->is_addr)); | |
bc54ef3d | 160 | is->is_promiscuous = 0; |
b50833d9 SL |
161 | for (wp = (short *)addr->hash, i = 0; i < 8; i++) |
162 | movow(wp++, ~0xf); | |
4a6cc6f7 SL |
163 | movow(&addr->bcastena[0], ~0xffff); |
164 | movow(&addr->bcastena[1], ~0xffff); | |
0d5152c1 SL |
165 | /* |
166 | * Allocate and map dual ported VERSAbus memory. | |
167 | */ | |
00aaa8a5 MK |
168 | if (vbmemalloc(32, (caddr_t)ui->ui_flags, |
169 | &is->is_map, &is->is_dpm) == 0) { | |
170 | printf("ace%d: can't allocate VERSAbus memory map\n", unit); | |
171 | return; | |
172 | } | |
0d5152c1 | 173 | |
bc54ef3d | 174 | ifp->if_init = aceinit; |
385e8f24 | 175 | ifp->if_output = ether_output; |
003ca8a0 | 176 | ifp->if_start = acestart; |
bc54ef3d SL |
177 | ifp->if_ioctl = aceioctl; |
178 | ifp->if_reset = acereset; | |
003ca8a0 | 179 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; |
bc54ef3d SL |
180 | if_attach(ifp); |
181 | } | |
182 | ||
bc54ef3d SL |
183 | /* |
184 | * Reset of interface after "system" reset. | |
185 | */ | |
186 | acereset(unit, vban) | |
187 | int unit, vban; | |
188 | { | |
189 | register struct vba_device *ui; | |
190 | ||
191 | if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 || | |
192 | ui->ui_vbanum != vban) | |
193 | return; | |
194 | printf(" ace%d", unit); | |
195 | aceinit(unit); | |
196 | } | |
197 | ||
198 | /* | |
199 | * Initialization of interface; clear recorded pending operations | |
200 | */ | |
201 | aceinit(unit) | |
202 | int unit; | |
203 | { | |
204 | register struct ace_softc *is = &ace_softc[unit]; | |
205 | register struct vba_device *ui = aceinfo[unit]; | |
206 | register struct acedevice *addr; | |
bc54ef3d | 207 | register short Csr; |
4a6cc6f7 | 208 | register int s; |
bc54ef3d | 209 | |
003ca8a0 | 210 | if (is->is_if.if_addrlist == (struct ifaddr *)0) |
bc54ef3d | 211 | return; |
003ca8a0 | 212 | if ((is->is_if.if_flags & IFF_RUNNING) == 0) { |
bc54ef3d SL |
213 | /* |
214 | * Reset the controller, initialize the recieve buffers, | |
215 | * and turn the controller on again and set board online. | |
216 | */ | |
217 | addr = (struct acedevice *)ui->ui_addr; | |
218 | s = splimp(); | |
4a6cc6f7 | 219 | movow(&addr->csr, CSR_RESET); |
bc54ef3d SL |
220 | DELAY(10000); |
221 | ||
222 | /* | |
0d5152c1 SL |
223 | * Clean up dpm since the controller might |
224 | * jumble dpm after reset. | |
bc54ef3d | 225 | */ |
0d5152c1 | 226 | acesetup(unit); |
4a6cc6f7 | 227 | movow(&addr->csr, CSR_GO); |
bc54ef3d SL |
228 | Csr = addr->csr; |
229 | if (Csr & CSR_ACTIVE) { | |
56ad8000 | 230 | movow(&addr->ivct, is->is_ivec); |
bc54ef3d | 231 | Csr |= CSR_IENA | is->is_promiscuous; |
4a6cc6f7 | 232 | movow(&addr->csr, Csr); |
bc54ef3d SL |
233 | is->is_flags = 0; |
234 | is->is_xcnt = 0; | |
4a6cc6f7 | 235 | is->is_if.if_flags |= IFF_RUNNING; |
bc54ef3d SL |
236 | } |
237 | splx(s); | |
238 | } | |
4a6cc6f7 | 239 | if (is->is_if.if_snd.ifq_head) |
003ca8a0 | 240 | acestart(&is->is_if); |
bc54ef3d SL |
241 | } |
242 | ||
243 | /* | |
244 | * Start output on interface. | |
245 | * Get another datagram to send off of the interface queue, | |
246 | * and map it to the interface before starting the output. | |
bc54ef3d | 247 | */ |
003ca8a0 MK |
248 | acestart(ifp) |
249 | register struct ifnet *ifp; | |
bc54ef3d SL |
250 | { |
251 | register struct tx_segment *txs; | |
4a6cc6f7 SL |
252 | register long len; |
253 | register int s; | |
bc54ef3d | 254 | struct mbuf *m; |
4a6cc6f7 | 255 | short retries; |
003ca8a0 | 256 | #define is ((struct ace_softc *)ifp) |
bc54ef3d SL |
257 | |
258 | again: | |
259 | txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11)); | |
260 | if (txs->tx_csr & TCS_TBFULL) { | |
261 | is->is_stats.tx_busy++; | |
003ca8a0 MK |
262 | ifp->if_flags |= IFF_OACTIVE; |
263 | return (0); | |
bc54ef3d | 264 | } |
4a6cc6f7 | 265 | s = splimp(); |
003ca8a0 | 266 | IF_DEQUEUE(&ifp->if_snd, m); |
4a6cc6f7 | 267 | splx(s); |
0d5152c1 | 268 | if (m == 0) { |
003ca8a0 MK |
269 | ifp->if_flags &= ~IFF_OACTIVE; |
270 | return (0); | |
0d5152c1 | 271 | } |
003ca8a0 | 272 | len = aceput(txs->tx_data, m); |
bc54ef3d SL |
273 | retries = txs->tx_csr & TCS_RTC; |
274 | if (retries > 0) | |
275 | acebakoff(is, txs, retries); | |
276 | ||
277 | /* | |
278 | * Ensure minimum packet length. | |
279 | * This makes the safe assumtion that there are no virtual holes | |
280 | * after the data. | |
281 | * For security, it might be wise to zero out the added bytes, | |
282 | * but we're mainly interested in speed at the moment. | |
283 | */ | |
bc54ef3d SL |
284 | if (len - sizeof (struct ether_header) < ETHERMIN) |
285 | len = ETHERMIN + sizeof (struct ether_header); | |
bc54ef3d SL |
286 | if (++is->is_txnext > SEG_MAX) |
287 | is->is_txnext = is->is_segboundry; | |
003ca8a0 | 288 | ifp->if_opackets++; |
bc54ef3d SL |
289 | is->is_xcnt++; |
290 | len = (len & 0x7fff) | TCS_TBFULL; | |
4a6cc6f7 | 291 | movow(txs, len); |
bc54ef3d | 292 | goto again; |
003ca8a0 | 293 | #undef is |
bc54ef3d SL |
294 | } |
295 | ||
296 | /* | |
297 | * Transmit done interrupt. | |
298 | */ | |
299 | acecint(unit) | |
300 | int unit; | |
301 | { | |
302 | register struct ace_softc *is = &ace_softc[unit]; | |
bc54ef3d | 303 | register struct tx_segment *txseg; |
4a6cc6f7 | 304 | short eostat; |
bc54ef3d SL |
305 | |
306 | if (is->is_xcnt <= 0) { | |
56ad8000 | 307 | log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n", |
bc54ef3d SL |
308 | unit, is->is_xcnt); |
309 | is->is_xcnt = 0; | |
4a6cc6f7 | 310 | if (is->is_if.if_snd.ifq_head) |
003ca8a0 | 311 | acestart(&is->is_if); |
bc54ef3d SL |
312 | return; |
313 | } | |
314 | is->is_xcnt--; | |
315 | txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm); | |
316 | eostat = txseg->tx_csr; | |
317 | if ((eostat & TCS_TBFULL) == 0) { | |
318 | is->is_stats.tx_retries += eostat & TCS_RTC; | |
319 | if (eostat & TCS_RTFAIL) { | |
320 | is->is_stats.tx_discarded++; | |
321 | is->is_if.if_oerrors++; | |
322 | } else | |
323 | is->is_stats.tx_datagrams++; | |
324 | if (++is->is_eoctr >= 16) | |
325 | is->is_eoctr = is->is_segboundry; | |
326 | } | |
4a6cc6f7 | 327 | if (is->is_if.if_snd.ifq_head) |
003ca8a0 | 328 | acestart(&is->is_if); |
bc54ef3d SL |
329 | } |
330 | ||
331 | /* | |
332 | * Ethernet interface receiver interrupt. | |
333 | * If input error just drop packet. | |
334 | * Otherwise purge input buffered data path and examine | |
335 | * packet to determine type. If can't determine length | |
336 | * from type, then have to drop packet. Othewise decapsulate | |
337 | * packet based on type and pass to type specific higher-level | |
338 | * input routine. | |
339 | */ | |
340 | acerint(unit) | |
341 | int unit; | |
342 | { | |
343 | register struct ace_softc *is = &ace_softc[unit]; | |
344 | register struct ifqueue *inq; | |
345 | register struct ether_header *ace; | |
346 | register struct rx_segment *rxseg; | |
bc54ef3d SL |
347 | int len, s, off, resid; |
348 | struct mbuf *m; | |
349 | short eistat; | |
350 | ||
4a6cc6f7 SL |
351 | if ((is->is_if.if_flags&IFF_RUNNING) == 0) |
352 | return; | |
bc54ef3d SL |
353 | again: |
354 | rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm); | |
355 | eistat = rxseg->rx_csr; | |
356 | if ((eistat & RCS_RBFULL) == 0) | |
357 | return; | |
358 | is->is_if.if_ipackets++; | |
359 | if (++is->is_eictr >= is->is_segboundry) | |
360 | is->is_eictr = 0; | |
361 | len = eistat & RCS_RBC; | |
362 | if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) || | |
363 | len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) { | |
364 | if (eistat & RCS_ROVRN) | |
365 | is->is_stats.rx_overruns++; | |
366 | if (eistat & RCS_RCRC) | |
367 | is->is_stats.rx_crc_errors++; | |
368 | if (eistat & RCS_RODD) | |
369 | is->is_stats.rx_align_errors++; | |
370 | if (len < ET_MINLEN) | |
371 | is->is_stats.rx_underruns++; | |
372 | if (len > ET_MAXLEN+CRC_SIZE) | |
373 | is->is_stats.rx_overruns++; | |
374 | is->is_if.if_ierrors++; | |
375 | rxseg->rx_csr = 0; | |
376 | return; | |
377 | } else | |
378 | is->is_stats.rx_datagrams++; | |
379 | ace = (struct ether_header *)rxseg->rx_data; | |
bc54ef3d | 380 | len -= sizeof (struct ether_header); |
bc54ef3d | 381 | /* |
4a6cc6f7 | 382 | * Deal with trailer protocol: if type is trailer |
bc54ef3d SL |
383 | * get true type from first 16-bit word past data. |
384 | * Remember that type was trailer by setting off. | |
385 | */ | |
386 | ace->ether_type = ntohs((u_short)ace->ether_type); | |
bc54ef3d SL |
387 | #define acedataaddr(ace, off, type) \ |
388 | ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off)))) | |
4a6cc6f7 SL |
389 | if (ace->ether_type >= ETHERTYPE_TRAIL && |
390 | ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
391 | off = (ace->ether_type - ETHERTYPE_TRAIL) * 512; | |
bc54ef3d SL |
392 | if (off >= ETHERMTU) |
393 | goto setup; /* sanity */ | |
394 | ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *)); | |
395 | resid = ntohs(*(acedataaddr(ace, off+2, u_short *))); | |
396 | if (off + resid > len) | |
397 | goto setup; /* sanity */ | |
398 | len = off + resid; | |
399 | } else | |
400 | off = 0; | |
401 | if (len == 0) | |
402 | goto setup; | |
403 | ||
404 | /* | |
405 | * Pull packet off interface. Off is nonzero if packet | |
406 | * has trailing header; aceget will then force this header | |
003ca8a0 | 407 | * information to be at the front. |
bc54ef3d | 408 | */ |
56ad8000 | 409 | m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if); |
bc54ef3d SL |
410 | if (m == 0) |
411 | goto setup; | |
bc54ef3d SL |
412 | switch (ace->ether_type) { |
413 | ||
414 | #ifdef INET | |
4a6cc6f7 | 415 | case ETHERTYPE_IP: |
bc54ef3d SL |
416 | schednetisr(NETISR_IP); |
417 | inq = &ipintrq; | |
418 | break; | |
56ad8000 | 419 | #endif |
bc54ef3d | 420 | |
4a6cc6f7 | 421 | case ETHERTYPE_ARP: |
bc54ef3d SL |
422 | arpinput(&is->is_ac, m); |
423 | goto setup; | |
4a6cc6f7 SL |
424 | #ifdef NS |
425 | case ETHERTYPE_NS: | |
426 | schednetisr(NETISR_NS); | |
427 | inq = &nsintrq; | |
428 | break; | |
429 | ||
bc54ef3d SL |
430 | #endif |
431 | default: | |
432 | m_freem(m); | |
433 | goto setup; | |
434 | } | |
435 | if (IF_QFULL(inq)) { | |
436 | IF_DROP(inq); | |
437 | m_freem(m); | |
438 | goto setup; | |
439 | } | |
440 | s = splimp(); | |
441 | IF_ENQUEUE(inq, m); | |
442 | splx(s); | |
443 | setup: | |
444 | rxseg->rx_csr = 0; | |
445 | goto again; | |
446 | } | |
447 | ||
bc54ef3d SL |
448 | /* |
449 | * Routine to copy from mbuf chain to transmit buffer on the VERSAbus | |
450 | * If packet size is less than the minimum legal size, | |
451 | * the buffer is expanded. We probably should zero out the extra | |
452 | * bytes for security, but that would slow things down. | |
453 | */ | |
003ca8a0 | 454 | aceput(txbuf, m) |
4a6cc6f7 | 455 | char *txbuf; |
bc54ef3d | 456 | struct mbuf *m; |
385e8f24 | 457 | #ifdef notdef |
bc54ef3d | 458 | { |
56ad8000 SL |
459 | register u_char *bp, *mcp; |
460 | register short *s1, *s2; | |
4a6cc6f7 | 461 | register u_int len; |
bc54ef3d | 462 | register struct mbuf *mp; |
4a6cc6f7 | 463 | int total; |
bc54ef3d | 464 | |
003ca8a0 | 465 | total = mp->m_pkthdr.len; |
4a6cc6f7 | 466 | bp = (u_char *)txbuf; |
385e8f24 | 467 | for (mp = m; mp; mp = mp->m_next) { |
bc54ef3d SL |
468 | len = mp->m_len; |
469 | if (len == 0) | |
470 | continue; | |
bc54ef3d SL |
471 | mcp = mtod(mp, u_char *); |
472 | if (((int)mcp & 01) && ((int)bp & 01)) { | |
473 | /* source & destination at odd addresses */ | |
4a6cc6f7 | 474 | movob(bp++, *mcp++); |
bc54ef3d SL |
475 | --len; |
476 | } | |
477 | if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { | |
385e8f24 | 478 | int l = len & 1; |
bc54ef3d SL |
479 | |
480 | s1 = (short *)bp; | |
481 | s2 = (short *)mcp; | |
003ca8a0 MK |
482 | len >>= 1; /* count # of shorts */ |
483 | while (len-- != 0) | |
4a6cc6f7 | 484 | movow(s1++, *s2++); |
385e8f24 KS |
485 | len = l; /* # remaining bytes */ |
486 | bp = (u_char *)s1; | |
487 | mcp = (u_char *)s2; | |
488 | } | |
489 | while (len-- != 0) | |
490 | movob(bp++, *mcp++); | |
491 | } | |
492 | m_freem(m); | |
493 | return (total); | |
494 | } | |
495 | #else | |
496 | { | |
497 | register u_char *bp, *mcp; | |
498 | register short *s1, *s2; | |
499 | register u_int len; | |
500 | register struct mbuf *mp; | |
501 | int total; | |
502 | ||
503 | total = 0; | |
504 | bp = (u_char *)txbuf; | |
505 | for (mp = m; (mp); mp = mp->m_next) { | |
506 | len = mp->m_len; | |
507 | if (len == 0) | |
508 | continue; | |
509 | total += len; | |
510 | mcp = mtod(mp, u_char *); | |
511 | if (((int)mcp & 01) && ((int)bp & 01)) { | |
512 | /* source & destination at odd addresses */ | |
513 | movob(bp++, *mcp++); | |
514 | --len; | |
515 | } | |
516 | if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { | |
517 | register u_int l; | |
518 | ||
519 | s1 = (short *)bp; | |
520 | s2 = (short *)mcp; | |
521 | l = len >> 1; /* count # of shorts */ | |
522 | while (l-- != 0) | |
523 | movow(s1++, *s2++); | |
524 | len &= 1; /* # remaining bytes */ | |
bc54ef3d SL |
525 | bp = (u_char *)s1; |
526 | mcp = (u_char *)s2; | |
527 | } | |
4a6cc6f7 SL |
528 | while (len-- != 0) |
529 | movob(bp++, *mcp++); | |
bc54ef3d SL |
530 | } |
531 | m_freem(m); | |
532 | return (total); | |
533 | } | |
385e8f24 | 534 | #endif |
bc54ef3d | 535 | |
bc54ef3d SL |
536 | /* |
537 | * Routine to copy from VERSAbus memory into mbufs. | |
538 | * | |
539 | * Warning: This makes the fairly safe assumption that | |
540 | * mbufs have even lengths. | |
541 | */ | |
542 | struct mbuf * | |
003ca8a0 | 543 | aceget(rxbuf, totlen, off, ifp) |
bc54ef3d | 544 | u_char *rxbuf; |
003ca8a0 | 545 | int totlen, off; |
56ad8000 | 546 | struct ifnet *ifp; |
bc54ef3d | 547 | { |
56ad8000 | 548 | register u_char *cp, *mcp; |
bc54ef3d | 549 | register struct mbuf *m; |
003ca8a0 | 550 | register int tlen; |
bc54ef3d | 551 | struct mbuf *top = 0, **mp = ⊤ |
003ca8a0 MK |
552 | int len; |
553 | u_char *packet_end; | |
554 | ||
555 | rxbuf += sizeof (struct ether_header); | |
556 | cp = rxbuf; | |
557 | packet_end = cp + totlen; | |
558 | if (off) { | |
559 | off += 2 * sizeof(u_short); | |
560 | totlen -= 2 *sizeof(u_short); | |
561 | cp = rxbuf + off; | |
562 | } | |
563 | ||
564 | MGETHDR(m, M_DONTWAIT, MT_DATA); | |
565 | if (m == 0) | |
566 | return (0); | |
567 | m->m_pkthdr.rcvif = ifp; | |
568 | m->m_pkthdr.len = totlen; | |
569 | m->m_len = MHLEN; | |
bc54ef3d | 570 | |
bc54ef3d | 571 | while (totlen > 0) { |
003ca8a0 MK |
572 | if (top) { |
573 | MGET(m, M_DONTWAIT, MT_DATA); | |
574 | if (m == 0) { | |
575 | m_freem(top); | |
576 | return (0); | |
577 | } | |
578 | m->m_len = MLEN; | |
579 | } | |
580 | len = min(totlen, (packet_end - cp)); | |
581 | if (len >= MINCLSIZE) { | |
582 | MCLGET(m, M_DONTWAIT); | |
583 | if (m->m_flags & M_EXT) | |
584 | m->m_len = len = min(len, MCLBYTES); | |
6adc8625 | 585 | else |
003ca8a0 | 586 | len = m->m_len; |
bc54ef3d | 587 | } else { |
56ad8000 | 588 | /* |
003ca8a0 | 589 | * Place initial small packet/header at end of mbuf. |
56ad8000 | 590 | */ |
003ca8a0 MK |
591 | if (len < m->m_len) { |
592 | if (top == 0 && len + max_linkhdr <= m->m_len) | |
593 | m->m_data += max_linkhdr; | |
594 | m->m_len = len; | |
595 | } else | |
596 | len = m->m_len; | |
56ad8000 | 597 | } |
003ca8a0 | 598 | mcp = mtod(m, u_char *); |
bc54ef3d SL |
599 | /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/ |
600 | /*cp += len; mcp += len;*/ | |
601 | tlen = len; | |
602 | if (((int)mcp & 01) && ((int)cp & 01)) { | |
603 | /* source & destination at odd addresses */ | |
604 | *mcp++ = *cp++; | |
605 | --tlen; | |
606 | } | |
607 | if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) { | |
608 | register short *s1, *s2; | |
609 | register int l; | |
610 | ||
611 | s1 = (short *)mcp; | |
612 | s2 = (short *)cp; | |
613 | l = tlen >> 1; /* count # of shorts */ | |
614 | while (l-- > 0) /* copy shorts */ | |
615 | *s1++ = *s2++; | |
616 | tlen &= 1; /* # remaining bytes */ | |
617 | mcp = (u_char *)s1; | |
618 | cp = (u_char *)s2; | |
619 | } | |
620 | while (tlen-- > 0) | |
621 | *mcp++ = *cp++; | |
622 | *mp = m; | |
623 | mp = &m->m_next; | |
003ca8a0 MK |
624 | totlen -= len; |
625 | if (cp == packet_end) | |
626 | cp = rxbuf; | |
bc54ef3d SL |
627 | } |
628 | return (top); | |
bc54ef3d SL |
629 | } |
630 | ||
b50833d9 SL |
631 | /* backoff table masks */ |
632 | short random_mask_tbl[16] = { | |
6adc8625 SL |
633 | 0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0, |
634 | 0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0 | |
b50833d9 SL |
635 | }; |
636 | ||
bc54ef3d SL |
637 | acebakoff(is, txseg, retries) |
638 | struct ace_softc *is; | |
639 | struct tx_segment *txseg; | |
640 | register int retries; | |
641 | { | |
642 | register short *pBakNum, random_num; | |
643 | short *pMask; | |
644 | ||
645 | pMask = &random_mask_tbl[0]; | |
646 | pBakNum = &txseg->tx_backoff[0]; | |
647 | while (--retries >= 0) { | |
648 | random_num = (is->is_currnd = (is->is_currnd * 18741)-13849); | |
649 | random_num &= *pMask++; | |
6adc8625 | 650 | *pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc); |
bc54ef3d SL |
651 | } |
652 | } | |
653 | ||
654 | /* | |
655 | * Process an ioctl request. | |
656 | */ | |
657 | aceioctl(ifp, cmd, data) | |
658 | register struct ifnet *ifp; | |
659 | int cmd; | |
660 | caddr_t data; | |
661 | { | |
4a6cc6f7 | 662 | register struct ifaddr *ifa = (struct ifaddr *)data; |
56ad8000 | 663 | struct acedevice *addr; |
4a6cc6f7 | 664 | int s = splimp(), error = 0; |
bc54ef3d | 665 | |
bc54ef3d SL |
666 | switch (cmd) { |
667 | ||
668 | case SIOCSIFADDR: | |
4a6cc6f7 | 669 | ifp->if_flags |= IFF_UP; |
385e8f24 | 670 | switch (ifa->ifa_addr->sa_family) { |
56ad8000 SL |
671 | #ifdef INET |
672 | case AF_INET: | |
673 | aceinit(ifp->if_unit); /* before arpwhohas */ | |
674 | ((struct arpcom *)ifp)->ac_ipaddr = | |
675 | IA_SIN(ifa)->sin_addr; | |
676 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
677 | break; | |
678 | #endif | |
679 | #ifdef NS | |
680 | case AF_NS: { | |
87a9540f SL |
681 | struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; |
682 | struct ace_softc *is = &ace_softc[ifp->if_unit]; | |
56ad8000 SL |
683 | |
684 | if (!ns_nullhost(*ina)) { | |
685 | ifp->if_flags &= ~IFF_RUNNING; | |
56ad8000 | 686 | addr = (struct acedevice *) |
87a9540f | 687 | aceinfo[ifp->if_unit]->ui_addr; |
56ad8000 SL |
688 | movow(&addr->csr, CSR_RESET); |
689 | DELAY(10000); | |
690 | /* set station address & copy addr to arp */ | |
b50833d9 | 691 | acesetaddr(ifp->if_unit, addr, |
56ad8000 SL |
692 | ina->x_host.c_host); |
693 | } else | |
87a9540f | 694 | ina->x_host = *(union ns_host *)is->is_addr; |
56ad8000 SL |
695 | aceinit(ifp->if_unit); |
696 | break; | |
697 | } | |
698 | #endif | |
699 | default: | |
700 | aceinit(ifp->if_unit); | |
701 | break; | |
702 | } | |
bc54ef3d SL |
703 | break; |
704 | ||
56ad8000 SL |
705 | case SIOCSIFFLAGS: |
706 | if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { | |
707 | addr = (struct acedevice *) | |
708 | (aceinfo[ifp->if_unit]->ui_addr); | |
709 | movow(&addr->csr, CSR_RESET); | |
710 | ifp->if_flags &= ~IFF_RUNNING; | |
711 | } else if (ifp->if_flags&IFF_UP && | |
712 | (ifp->if_flags&IFF_RUNNING) == 0) | |
713 | aceinit(ifp->if_unit); | |
bc54ef3d | 714 | break; |
bc54ef3d SL |
715 | |
716 | default: | |
717 | error = EINVAL; | |
718 | } | |
719 | splx(s); | |
720 | return (error); | |
721 | } | |
722 | ||
b50833d9 SL |
723 | /* |
724 | * Set the on-board station address, then read it back | |
725 | * to initialize the address used by ARP (among others). | |
726 | */ | |
727 | acesetaddr(unit, addr, station) | |
728 | short unit; | |
729 | struct acedevice *addr; | |
9d61b7ff | 730 | u_char *station; |
b50833d9 SL |
731 | { |
732 | struct ace_softc *is = &ace_softc[unit]; | |
733 | register short *wp, i; | |
734 | ||
735 | for (wp = (short *)addr->station, i = 0; i < 6; i++) | |
736 | movow(wp++, ~*station++); | |
737 | for (wp = (short *)addr->station, i = 0; i < 6; i++) | |
738 | is->is_addr[i] = ~*wp++; | |
739 | printf("ace%d: hardware address %s\n", unit, | |
740 | ether_sprintf(is->is_addr)); | |
741 | } | |
742 | ||
743 | /* | |
744 | * Setup the device for use. Initialize dual-ported memory, | |
745 | * backoff parameters, and various other software state. | |
746 | */ | |
0d5152c1 | 747 | acesetup(unit) |
bc54ef3d SL |
748 | int unit; |
749 | { | |
750 | register struct ace_softc *is = &ace_softc[unit]; | |
bc54ef3d | 751 | register char *pData1; |
0d5152c1 SL |
752 | register short i; |
753 | struct acedevice *addr; | |
bc54ef3d | 754 | |
0d5152c1 | 755 | bzero(is->is_dpm, 16384*2); |
bc54ef3d | 756 | is->is_currnd = 49123; |
0d5152c1 | 757 | addr = (struct acedevice *)aceinfo[unit]->ui_addr; |
bc54ef3d | 758 | is->is_segboundry = (addr->segb >> 11) & 0xf; |
0d5152c1 | 759 | pData1 = is->is_dpm + (is->is_segboundry << 11); |
bc54ef3d SL |
760 | for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) { |
761 | acebakoff(is, (struct tx_segment *)pData1, 15); | |
762 | pData1 += sizeof (struct tx_segment); | |
763 | } | |
764 | is->is_eictr = 0; | |
765 | is->is_eoctr = is->is_txnext = is->is_segboundry; | |
766 | bzero((char *)&is->is_stats, sizeof (is->is_stats)); | |
767 | } | |
768 | #endif |