1dc228261Skarels /*
2dc228261Skarels * Copyright (c) 1988 Regents of the University of California.
3dc228261Skarels * All rights reserved.
4dc228261Skarels *
5f6bb277dSkarels * This code is derived from software contributed to Berkeley by
6f6bb277dSkarels * Computer Consoles Inc.
7f6bb277dSkarels *
89b7be0f7Sbostic * %sccs.include.redist.c%
9dc228261Skarels *
10*db0af71cSbostic * @(#)if_ace.c 7.8 (Berkeley) 12/16/90
11dc228261Skarels */
12226df7bfSsam
13226df7bfSsam /*
14226df7bfSsam * ACC VERSAbus Ethernet controller
15226df7bfSsam */
16226df7bfSsam #include "ace.h"
17226df7bfSsam #if NACE > 0
18226df7bfSsam
19*db0af71cSbostic #include "sys/param.h"
20*db0af71cSbostic #include "sys/systm.h"
21*db0af71cSbostic #include "sys/malloc.h"
22*db0af71cSbostic #include "sys/mbuf.h"
23*db0af71cSbostic #include "sys/buf.h"
24*db0af71cSbostic #include "sys/protosw.h"
25*db0af71cSbostic #include "sys/socket.h"
26*db0af71cSbostic #include "sys/vmmac.h"
27*db0af71cSbostic #include "sys/ioctl.h"
28*db0af71cSbostic #include "sys/errno.h"
29*db0af71cSbostic #include "sys/vmparam.h"
30*db0af71cSbostic #include "sys/syslog.h"
31226df7bfSsam
32*db0af71cSbostic #include "net/if.h"
33*db0af71cSbostic #include "net/netisr.h"
34*db0af71cSbostic #include "net/route.h"
357a0e3b47Ssam #ifdef INET
36*db0af71cSbostic #include "netinet/in.h"
37*db0af71cSbostic #include "netinet/in_systm.h"
38*db0af71cSbostic #include "netinet/in_var.h"
39*db0af71cSbostic #include "netinet/ip.h"
40*db0af71cSbostic #include "netinet/ip_var.h"
41*db0af71cSbostic #include "netinet/if_ether.h"
427a0e3b47Ssam #endif
437a0e3b47Ssam #ifdef NS
44*db0af71cSbostic #include "netns/ns.h"
45*db0af71cSbostic #include "netns/ns_if.h"
467a0e3b47Ssam #endif
47226df7bfSsam
48*db0af71cSbostic #include "../include/cpu.h"
49*db0af71cSbostic #include "../include/pte.h"
5070fe45acSsam
51*db0af71cSbostic #include "../include/mtpr.h"
52*db0af71cSbostic #include "../if/if_acereg.h"
53*db0af71cSbostic #include "../vba/vbavar.h"
54226df7bfSsam
55f6bb277dSkarels int aceprobe(), aceattach(), acerint(), acecint(), acestart();
56226df7bfSsam struct vba_device *aceinfo[NACE];
57c5249c6dSsam long acestd[] = { 0 };
58226df7bfSsam struct vba_driver acedriver =
59226df7bfSsam { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 };
60226df7bfSsam
61226df7bfSsam int aceinit(), aceoutput(), aceioctl(), acereset();
62226df7bfSsam struct mbuf *aceget();
63226df7bfSsam
64226df7bfSsam /*
65226df7bfSsam * Ethernet software status per interface.
66226df7bfSsam *
67226df7bfSsam * Each interface is referenced by a network interface structure,
68226df7bfSsam * is_if, which the routing code uses to locate the interface.
69226df7bfSsam * This structure contains the output queue for the interface, its address, ...
70226df7bfSsam */
71226df7bfSsam struct ace_softc {
72226df7bfSsam struct arpcom is_ac; /* Ethernet common part */
73226df7bfSsam #define is_if is_ac.ac_if /* network-visible interface */
74226df7bfSsam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */
75226df7bfSsam short is_flags;
76226df7bfSsam #define ACEF_OACTIVE 0x1 /* output is active */
77226df7bfSsam #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */
78226df7bfSsam short is_promiscuous; /* true is enabled */
79226df7bfSsam short is_segboundry; /* first TX Seg in dpm */
80226df7bfSsam short is_eictr; /* Rx segment tracking ctr */
81226df7bfSsam short is_eoctr; /* Tx segment tracking ctr */
82226df7bfSsam short is_txnext; /* Next available Tx segment */
83226df7bfSsam short is_currnd; /* current random backoff */
84226df7bfSsam struct ace_stats is_stats; /* holds board statistics */
85226df7bfSsam short is_xcnt; /* count xmitted segments to be acked
86226df7bfSsam by the controller */
877a0e3b47Ssam long is_ivec; /* autoconfig interrupt vector base */
88a29de3d6Ssam struct pte *is_map; /* pte map for dual ported memory */
89a29de3d6Ssam caddr_t is_dpm; /* address of mapped memory */
90226df7bfSsam } ace_softc[NACE];
91226df7bfSsam extern struct ifnet loif;
92226df7bfSsam
aceprobe(reg,vi)937a0e3b47Ssam aceprobe(reg, vi)
94226df7bfSsam caddr_t reg;
957a0e3b47Ssam struct vba_device *vi;
96226df7bfSsam {
977a0e3b47Ssam register br, cvec; /* must be r12, r11 */
987a0e3b47Ssam struct acedevice *ap = (struct acedevice *)reg;
997a0e3b47Ssam struct ace_softc *is = &ace_softc[vi->ui_unit];
100226df7bfSsam
101226df7bfSsam #ifdef lint
1021c94db25Ssam br = 0; cvec = br; br = cvec;
103226df7bfSsam acerint(0); acecint(0);
104226df7bfSsam #endif
105226df7bfSsam if (badaddr(reg, 2))
106226df7bfSsam return (0);
1077a0e3b47Ssam movow(&ap->csr, CSR_RESET);
108226df7bfSsam DELAY(10000);
1097a0e3b47Ssam #ifdef notdef
1107a0e3b47Ssam /*
1117a0e3b47Ssam * Select two spaces for the interrupts aligned to an
1127a0e3b47Ssam * eight vector boundary and fitting in 8 bits (as
1137a0e3b47Ssam * required by the controller) -- YECH. The controller
1147a0e3b47Ssam * will be notified later at initialization time.
1157a0e3b47Ssam */
1167a0e3b47Ssam if ((vi->ui_hd->vh_lastiv -= 2) > 0xff)
1177a0e3b47Ssam vi->ui_hd->vh_lastiv = 0x200;
1187a0e3b47Ssam is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7;
1197a0e3b47Ssam #else
1207a0e3b47Ssam is->is_ivec = 0x90+vi->ui_unit*8;
1217a0e3b47Ssam #endif
1227a0e3b47Ssam br = 0x14, cvec = is->is_ivec; /* XXX */
1237a0e3b47Ssam return (sizeof (*ap));
124226df7bfSsam }
125226df7bfSsam
126226df7bfSsam /*
127226df7bfSsam * Interface exists: make available by filling in network interface
128226df7bfSsam * record. System will initialize the interface when it is ready
129226df7bfSsam * to accept packets.
130226df7bfSsam */
131226df7bfSsam aceattach(ui)
132226df7bfSsam struct vba_device *ui;
133226df7bfSsam {
134226df7bfSsam register short unit = ui->ui_unit;
135226df7bfSsam register struct ace_softc *is = &ace_softc[unit];
136226df7bfSsam register struct ifnet *ifp = &is->is_if;
137226df7bfSsam register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
138226df7bfSsam register short *wp, i;
139226df7bfSsam
140226df7bfSsam ifp->if_unit = unit;
141226df7bfSsam ifp->if_name = "ace";
142226df7bfSsam ifp->if_mtu = ETHERMTU;
143226df7bfSsam /*
14430e6d859Ssam * Get station's addresses and set multicast hash table.
145226df7bfSsam */
14630e6d859Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++)
14730e6d859Ssam is->is_addr[i] = ~*wp++;
14830e6d859Ssam printf("ace%d: hardware address %s\n", unit,
14930e6d859Ssam ether_sprintf(is->is_addr));
150226df7bfSsam is->is_promiscuous = 0;
15130e6d859Ssam for (wp = (short *)addr->hash, i = 0; i < 8; i++)
15230e6d859Ssam movow(wp++, ~0xf);
153c0fcc33dSsam movow(&addr->bcastena[0], ~0xffff);
154c0fcc33dSsam movow(&addr->bcastena[1], ~0xffff);
155a29de3d6Ssam /*
156a29de3d6Ssam * Allocate and map dual ported VERSAbus memory.
157a29de3d6Ssam */
158904c9c54Skarels if (vbmemalloc(32, (caddr_t)ui->ui_flags,
159904c9c54Skarels &is->is_map, &is->is_dpm) == 0) {
160904c9c54Skarels printf("ace%d: can't allocate VERSAbus memory map\n", unit);
161904c9c54Skarels return;
162904c9c54Skarels }
163a29de3d6Ssam
164226df7bfSsam ifp->if_init = aceinit;
1659d42a7deSsklower ifp->if_output = ether_output;
166f6bb277dSkarels ifp->if_start = acestart;
167226df7bfSsam ifp->if_ioctl = aceioctl;
168226df7bfSsam ifp->if_reset = acereset;
169f6bb277dSkarels ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
170226df7bfSsam if_attach(ifp);
171226df7bfSsam }
172226df7bfSsam
173226df7bfSsam /*
174226df7bfSsam * Reset of interface after "system" reset.
175226df7bfSsam */
acereset(unit,vban)176226df7bfSsam acereset(unit, vban)
177226df7bfSsam int unit, vban;
178226df7bfSsam {
179226df7bfSsam register struct vba_device *ui;
180226df7bfSsam
181226df7bfSsam if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 ||
182226df7bfSsam ui->ui_vbanum != vban)
183226df7bfSsam return;
184226df7bfSsam printf(" ace%d", unit);
185226df7bfSsam aceinit(unit);
186226df7bfSsam }
187226df7bfSsam
188226df7bfSsam /*
189226df7bfSsam * Initialization of interface; clear recorded pending operations
190226df7bfSsam */
aceinit(unit)191226df7bfSsam aceinit(unit)
192226df7bfSsam int unit;
193226df7bfSsam {
194226df7bfSsam register struct ace_softc *is = &ace_softc[unit];
195226df7bfSsam register struct vba_device *ui = aceinfo[unit];
196226df7bfSsam register struct acedevice *addr;
197226df7bfSsam register short Csr;
198c0fcc33dSsam register int s;
199226df7bfSsam
200f6bb277dSkarels if (is->is_if.if_addrlist == (struct ifaddr *)0)
201226df7bfSsam return;
202f6bb277dSkarels if ((is->is_if.if_flags & IFF_RUNNING) == 0) {
203226df7bfSsam /*
204226df7bfSsam * Reset the controller, initialize the recieve buffers,
205226df7bfSsam * and turn the controller on again and set board online.
206226df7bfSsam */
207226df7bfSsam addr = (struct acedevice *)ui->ui_addr;
208226df7bfSsam s = splimp();
209c0fcc33dSsam movow(&addr->csr, CSR_RESET);
210226df7bfSsam DELAY(10000);
211226df7bfSsam
212226df7bfSsam /*
213a29de3d6Ssam * Clean up dpm since the controller might
214a29de3d6Ssam * jumble dpm after reset.
215226df7bfSsam */
216a29de3d6Ssam acesetup(unit);
217c0fcc33dSsam movow(&addr->csr, CSR_GO);
218226df7bfSsam Csr = addr->csr;
219226df7bfSsam if (Csr & CSR_ACTIVE) {
2207a0e3b47Ssam movow(&addr->ivct, is->is_ivec);
221226df7bfSsam Csr |= CSR_IENA | is->is_promiscuous;
222c0fcc33dSsam movow(&addr->csr, Csr);
223226df7bfSsam is->is_flags = 0;
224226df7bfSsam is->is_xcnt = 0;
225c0fcc33dSsam is->is_if.if_flags |= IFF_RUNNING;
226226df7bfSsam }
227226df7bfSsam splx(s);
228226df7bfSsam }
229c0fcc33dSsam if (is->is_if.if_snd.ifq_head)
230f6bb277dSkarels acestart(&is->is_if);
231226df7bfSsam }
232226df7bfSsam
233226df7bfSsam /*
234226df7bfSsam * Start output on interface.
235226df7bfSsam * Get another datagram to send off of the interface queue,
236226df7bfSsam * and map it to the interface before starting the output.
237226df7bfSsam */
acestart(ifp)238f6bb277dSkarels acestart(ifp)
239f6bb277dSkarels register struct ifnet *ifp;
240226df7bfSsam {
241226df7bfSsam register struct tx_segment *txs;
242c0fcc33dSsam register long len;
243c0fcc33dSsam register int s;
244226df7bfSsam struct mbuf *m;
245c0fcc33dSsam short retries;
246f6bb277dSkarels #define is ((struct ace_softc *)ifp)
247226df7bfSsam
248226df7bfSsam again:
249226df7bfSsam txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11));
250226df7bfSsam if (txs->tx_csr & TCS_TBFULL) {
251226df7bfSsam is->is_stats.tx_busy++;
252f6bb277dSkarels ifp->if_flags |= IFF_OACTIVE;
253f6bb277dSkarels return (0);
254226df7bfSsam }
255c0fcc33dSsam s = splimp();
256f6bb277dSkarels IF_DEQUEUE(&ifp->if_snd, m);
257c0fcc33dSsam splx(s);
258a29de3d6Ssam if (m == 0) {
259f6bb277dSkarels ifp->if_flags &= ~IFF_OACTIVE;
260f6bb277dSkarels return (0);
261a29de3d6Ssam }
262f6bb277dSkarels len = aceput(txs->tx_data, m);
263226df7bfSsam retries = txs->tx_csr & TCS_RTC;
264226df7bfSsam if (retries > 0)
265226df7bfSsam acebakoff(is, txs, retries);
266226df7bfSsam
267226df7bfSsam /*
268226df7bfSsam * Ensure minimum packet length.
269226df7bfSsam * This makes the safe assumtion that there are no virtual holes
270226df7bfSsam * after the data.
271226df7bfSsam * For security, it might be wise to zero out the added bytes,
272226df7bfSsam * but we're mainly interested in speed at the moment.
273226df7bfSsam */
274226df7bfSsam if (len - sizeof (struct ether_header) < ETHERMIN)
275226df7bfSsam len = ETHERMIN + sizeof (struct ether_header);
276226df7bfSsam if (++is->is_txnext > SEG_MAX)
277226df7bfSsam is->is_txnext = is->is_segboundry;
278f6bb277dSkarels ifp->if_opackets++;
279226df7bfSsam is->is_xcnt++;
280226df7bfSsam len = (len & 0x7fff) | TCS_TBFULL;
281c0fcc33dSsam movow(txs, len);
282226df7bfSsam goto again;
283f6bb277dSkarels #undef is
284226df7bfSsam }
285226df7bfSsam
286226df7bfSsam /*
287226df7bfSsam * Transmit done interrupt.
288226df7bfSsam */
acecint(unit)289226df7bfSsam acecint(unit)
290226df7bfSsam int unit;
291226df7bfSsam {
292226df7bfSsam register struct ace_softc *is = &ace_softc[unit];
293226df7bfSsam register struct tx_segment *txseg;
294c0fcc33dSsam short eostat;
295226df7bfSsam
296226df7bfSsam if (is->is_xcnt <= 0) {
2977a0e3b47Ssam log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n",
298226df7bfSsam unit, is->is_xcnt);
299226df7bfSsam is->is_xcnt = 0;
300c0fcc33dSsam if (is->is_if.if_snd.ifq_head)
301f6bb277dSkarels acestart(&is->is_if);
302226df7bfSsam return;
303226df7bfSsam }
304226df7bfSsam is->is_xcnt--;
305226df7bfSsam txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm);
306226df7bfSsam eostat = txseg->tx_csr;
307226df7bfSsam if ((eostat & TCS_TBFULL) == 0) {
308226df7bfSsam is->is_stats.tx_retries += eostat & TCS_RTC;
309226df7bfSsam if (eostat & TCS_RTFAIL) {
310226df7bfSsam is->is_stats.tx_discarded++;
311226df7bfSsam is->is_if.if_oerrors++;
312226df7bfSsam } else
313226df7bfSsam is->is_stats.tx_datagrams++;
314226df7bfSsam if (++is->is_eoctr >= 16)
315226df7bfSsam is->is_eoctr = is->is_segboundry;
316226df7bfSsam }
317c0fcc33dSsam if (is->is_if.if_snd.ifq_head)
318f6bb277dSkarels acestart(&is->is_if);
319226df7bfSsam }
320226df7bfSsam
321226df7bfSsam /*
322226df7bfSsam * Ethernet interface receiver interrupt.
323226df7bfSsam * If input error just drop packet.
324226df7bfSsam * Otherwise purge input buffered data path and examine
325226df7bfSsam * packet to determine type. If can't determine length
326226df7bfSsam * from type, then have to drop packet. Othewise decapsulate
327226df7bfSsam * packet based on type and pass to type specific higher-level
328226df7bfSsam * input routine.
329226df7bfSsam */
acerint(unit)330226df7bfSsam acerint(unit)
331226df7bfSsam int unit;
332226df7bfSsam {
333226df7bfSsam register struct ace_softc *is = &ace_softc[unit];
334226df7bfSsam register struct ifqueue *inq;
335226df7bfSsam register struct ether_header *ace;
336226df7bfSsam register struct rx_segment *rxseg;
337226df7bfSsam int len, s, off, resid;
338226df7bfSsam struct mbuf *m;
339226df7bfSsam short eistat;
340226df7bfSsam
341c0fcc33dSsam if ((is->is_if.if_flags&IFF_RUNNING) == 0)
342c0fcc33dSsam return;
343226df7bfSsam again:
344226df7bfSsam rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm);
345226df7bfSsam eistat = rxseg->rx_csr;
346226df7bfSsam if ((eistat & RCS_RBFULL) == 0)
347226df7bfSsam return;
348226df7bfSsam is->is_if.if_ipackets++;
349226df7bfSsam if (++is->is_eictr >= is->is_segboundry)
350226df7bfSsam is->is_eictr = 0;
351226df7bfSsam len = eistat & RCS_RBC;
352226df7bfSsam if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) ||
353226df7bfSsam len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) {
354226df7bfSsam if (eistat & RCS_ROVRN)
355226df7bfSsam is->is_stats.rx_overruns++;
356226df7bfSsam if (eistat & RCS_RCRC)
357226df7bfSsam is->is_stats.rx_crc_errors++;
358226df7bfSsam if (eistat & RCS_RODD)
359226df7bfSsam is->is_stats.rx_align_errors++;
360226df7bfSsam if (len < ET_MINLEN)
361226df7bfSsam is->is_stats.rx_underruns++;
362226df7bfSsam if (len > ET_MAXLEN+CRC_SIZE)
363226df7bfSsam is->is_stats.rx_overruns++;
364226df7bfSsam is->is_if.if_ierrors++;
365226df7bfSsam rxseg->rx_csr = 0;
366226df7bfSsam return;
367226df7bfSsam } else
368226df7bfSsam is->is_stats.rx_datagrams++;
369226df7bfSsam ace = (struct ether_header *)rxseg->rx_data;
370226df7bfSsam len -= sizeof (struct ether_header);
371226df7bfSsam /*
372c0fcc33dSsam * Deal with trailer protocol: if type is trailer
373226df7bfSsam * get true type from first 16-bit word past data.
374226df7bfSsam * Remember that type was trailer by setting off.
375226df7bfSsam */
376226df7bfSsam ace->ether_type = ntohs((u_short)ace->ether_type);
377226df7bfSsam #define acedataaddr(ace, off, type) \
378226df7bfSsam ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off))))
379c0fcc33dSsam if (ace->ether_type >= ETHERTYPE_TRAIL &&
380c0fcc33dSsam ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
381c0fcc33dSsam off = (ace->ether_type - ETHERTYPE_TRAIL) * 512;
382226df7bfSsam if (off >= ETHERMTU)
383226df7bfSsam goto setup; /* sanity */
384226df7bfSsam ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *));
385226df7bfSsam resid = ntohs(*(acedataaddr(ace, off+2, u_short *)));
386226df7bfSsam if (off + resid > len)
387226df7bfSsam goto setup; /* sanity */
388226df7bfSsam len = off + resid;
389226df7bfSsam } else
390226df7bfSsam off = 0;
391226df7bfSsam if (len == 0)
392226df7bfSsam goto setup;
393226df7bfSsam
394226df7bfSsam /*
395226df7bfSsam * Pull packet off interface. Off is nonzero if packet
396226df7bfSsam * has trailing header; aceget will then force this header
397f6bb277dSkarels * information to be at the front.
398226df7bfSsam */
3997a0e3b47Ssam m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if);
400692a5a97Skarels if (m)
401692a5a97Skarels ether_input(&is->is_if, ace, m);
402226df7bfSsam setup:
403226df7bfSsam rxseg->rx_csr = 0;
404226df7bfSsam goto again;
405226df7bfSsam }
406226df7bfSsam
407226df7bfSsam /*
408226df7bfSsam * Routine to copy from mbuf chain to transmit buffer on the VERSAbus
409226df7bfSsam * If packet size is less than the minimum legal size,
410226df7bfSsam * the buffer is expanded. We probably should zero out the extra
411226df7bfSsam * bytes for security, but that would slow things down.
412226df7bfSsam */
aceput(txbuf,m)413f6bb277dSkarels aceput(txbuf, m)
414c0fcc33dSsam char *txbuf;
415226df7bfSsam struct mbuf *m;
4169d42a7deSsklower #ifdef notdef
417226df7bfSsam {
4187a0e3b47Ssam register u_char *bp, *mcp;
4197a0e3b47Ssam register short *s1, *s2;
420c0fcc33dSsam register u_int len;
421226df7bfSsam register struct mbuf *mp;
422c0fcc33dSsam int total;
423226df7bfSsam
424f6bb277dSkarels total = mp->m_pkthdr.len;
425c0fcc33dSsam bp = (u_char *)txbuf;
4269d42a7deSsklower for (mp = m; mp; mp = mp->m_next) {
427226df7bfSsam len = mp->m_len;
428226df7bfSsam if (len == 0)
429226df7bfSsam continue;
430226df7bfSsam mcp = mtod(mp, u_char *);
431226df7bfSsam if (((int)mcp & 01) && ((int)bp & 01)) {
432226df7bfSsam /* source & destination at odd addresses */
433c0fcc33dSsam movob(bp++, *mcp++);
434226df7bfSsam --len;
435226df7bfSsam }
436226df7bfSsam if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) {
4379d42a7deSsklower int l = len & 1;
438226df7bfSsam
439226df7bfSsam s1 = (short *)bp;
440226df7bfSsam s2 = (short *)mcp;
441f6bb277dSkarels len >>= 1; /* count # of shorts */
442f6bb277dSkarels while (len-- != 0)
443c0fcc33dSsam movow(s1++, *s2++);
4449d42a7deSsklower len = l; /* # remaining bytes */
445226df7bfSsam bp = (u_char *)s1;
446226df7bfSsam mcp = (u_char *)s2;
447226df7bfSsam }
448c0fcc33dSsam while (len-- != 0)
449c0fcc33dSsam movob(bp++, *mcp++);
450226df7bfSsam }
451226df7bfSsam m_freem(m);
452226df7bfSsam return (total);
453226df7bfSsam }
4549d42a7deSsklower #else
4559d42a7deSsklower {
4569d42a7deSsklower register u_char *bp, *mcp;
4579d42a7deSsklower register short *s1, *s2;
4589d42a7deSsklower register u_int len;
4599d42a7deSsklower register struct mbuf *mp;
4609d42a7deSsklower int total;
4619d42a7deSsklower
4629d42a7deSsklower total = 0;
4639d42a7deSsklower bp = (u_char *)txbuf;
4649d42a7deSsklower for (mp = m; (mp); mp = mp->m_next) {
4659d42a7deSsklower len = mp->m_len;
4669d42a7deSsklower if (len == 0)
4679d42a7deSsklower continue;
4689d42a7deSsklower total += len;
4699d42a7deSsklower mcp = mtod(mp, u_char *);
4709d42a7deSsklower if (((int)mcp & 01) && ((int)bp & 01)) {
4719d42a7deSsklower /* source & destination at odd addresses */
4729d42a7deSsklower movob(bp++, *mcp++);
4739d42a7deSsklower --len;
4749d42a7deSsklower }
4759d42a7deSsklower if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) {
4769d42a7deSsklower register u_int l;
4779d42a7deSsklower
4789d42a7deSsklower s1 = (short *)bp;
4799d42a7deSsklower s2 = (short *)mcp;
4809d42a7deSsklower l = len >> 1; /* count # of shorts */
4819d42a7deSsklower while (l-- != 0)
4829d42a7deSsklower movow(s1++, *s2++);
4839d42a7deSsklower len &= 1; /* # remaining bytes */
4849d42a7deSsklower bp = (u_char *)s1;
4859d42a7deSsklower mcp = (u_char *)s2;
4869d42a7deSsklower }
4879d42a7deSsklower while (len-- != 0)
4889d42a7deSsklower movob(bp++, *mcp++);
4899d42a7deSsklower }
4909d42a7deSsklower m_freem(m);
4919d42a7deSsklower return (total);
4929d42a7deSsklower }
4939d42a7deSsklower #endif
494226df7bfSsam
495226df7bfSsam /*
496226df7bfSsam * Routine to copy from VERSAbus memory into mbufs.
497226df7bfSsam *
498226df7bfSsam * Warning: This makes the fairly safe assumption that
499226df7bfSsam * mbufs have even lengths.
500226df7bfSsam */
501226df7bfSsam struct mbuf *
aceget(rxbuf,totlen,off,ifp)502f6bb277dSkarels aceget(rxbuf, totlen, off, ifp)
503226df7bfSsam u_char *rxbuf;
504f6bb277dSkarels int totlen, off;
5057a0e3b47Ssam struct ifnet *ifp;
506226df7bfSsam {
5077a0e3b47Ssam register u_char *cp, *mcp;
508226df7bfSsam register struct mbuf *m;
509f6bb277dSkarels register int tlen;
510226df7bfSsam struct mbuf *top = 0, **mp = ⊤
511f6bb277dSkarels int len;
512f6bb277dSkarels u_char *packet_end;
513226df7bfSsam
514f6bb277dSkarels rxbuf += sizeof (struct ether_header);
515f6bb277dSkarels cp = rxbuf;
516f6bb277dSkarels packet_end = cp + totlen;
517226df7bfSsam if (off) {
518f6bb277dSkarels off += 2 * sizeof(u_short);
519f6bb277dSkarels totlen -= 2 * sizeof(u_short);
520f6bb277dSkarels cp = rxbuf + off;
521f6bb277dSkarels }
522f6bb277dSkarels
523f6bb277dSkarels MGETHDR(m, M_DONTWAIT, MT_DATA);
524f6bb277dSkarels if (m == 0)
525f6bb277dSkarels return (0);
526f6bb277dSkarels m->m_pkthdr.rcvif = ifp;
527f6bb277dSkarels m->m_pkthdr.len = totlen;
528f6bb277dSkarels m->m_len = MHLEN;
529f6bb277dSkarels
530f6bb277dSkarels while (totlen > 0) {
531f6bb277dSkarels if (top) {
532f6bb277dSkarels MGET(m, M_DONTWAIT, MT_DATA);
533f6bb277dSkarels if (m == 0) {
534f6bb277dSkarels m_freem(top);
535f6bb277dSkarels return (0);
536f6bb277dSkarels }
537f6bb277dSkarels m->m_len = MLEN;
538f6bb277dSkarels }
539f6bb277dSkarels len = min(totlen, (packet_end - cp));
540f6bb277dSkarels if (len >= MINCLSIZE) {
541f6bb277dSkarels MCLGET(m, M_DONTWAIT);
542f6bb277dSkarels if (m->m_flags & M_EXT)
543f6bb277dSkarels m->m_len = len = min(len, MCLBYTES);
544d5633a91Ssam else
545f6bb277dSkarels len = m->m_len;
546226df7bfSsam } else {
547f6bb277dSkarels /*
548f6bb277dSkarels * Place initial small packet/header at end of mbuf.
549f6bb277dSkarels */
550f6bb277dSkarels if (len < m->m_len) {
551f6bb277dSkarels if (top == 0 && len + max_linkhdr <= m->m_len)
552f6bb277dSkarels m->m_data += max_linkhdr;
553f6bb277dSkarels m->m_len = len;
554f6bb277dSkarels } else
555f6bb277dSkarels len = m->m_len;
556226df7bfSsam }
557226df7bfSsam mcp = mtod(m, u_char *);
558226df7bfSsam /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/
559226df7bfSsam /*cp += len; mcp += len;*/
560226df7bfSsam tlen = len;
561226df7bfSsam if (((int)mcp & 01) && ((int)cp & 01)) {
562226df7bfSsam /* source & destination at odd addresses */
563226df7bfSsam *mcp++ = *cp++;
564226df7bfSsam --tlen;
565226df7bfSsam }
566226df7bfSsam if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) {
567226df7bfSsam register short *s1, *s2;
568226df7bfSsam register int l;
569226df7bfSsam
570226df7bfSsam s1 = (short *)mcp;
571226df7bfSsam s2 = (short *)cp;
572226df7bfSsam l = tlen >> 1; /* count # of shorts */
573226df7bfSsam while (l-- > 0) /* copy shorts */
574226df7bfSsam *s1++ = *s2++;
575226df7bfSsam tlen &= 1; /* # remaining bytes */
576226df7bfSsam mcp = (u_char *)s1;
577226df7bfSsam cp = (u_char *)s2;
578226df7bfSsam }
579226df7bfSsam while (tlen-- > 0)
580226df7bfSsam *mcp++ = *cp++;
581226df7bfSsam *mp = m;
582226df7bfSsam mp = &m->m_next;
583226df7bfSsam totlen -= len;
584f6bb277dSkarels if (cp == packet_end)
585f6bb277dSkarels cp = rxbuf;
586226df7bfSsam }
587226df7bfSsam return (top);
588226df7bfSsam }
589226df7bfSsam
59030e6d859Ssam /* backoff table masks */
59130e6d859Ssam short random_mask_tbl[16] = {
592d5633a91Ssam 0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0,
593d5633a91Ssam 0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0
59430e6d859Ssam };
59530e6d859Ssam
596226df7bfSsam acebakoff(is, txseg, retries)
597226df7bfSsam struct ace_softc *is;
598226df7bfSsam struct tx_segment *txseg;
599226df7bfSsam register int retries;
600226df7bfSsam {
601226df7bfSsam register short *pBakNum, random_num;
602226df7bfSsam short *pMask;
603226df7bfSsam
604226df7bfSsam pMask = &random_mask_tbl[0];
605226df7bfSsam pBakNum = &txseg->tx_backoff[0];
606226df7bfSsam while (--retries >= 0) {
607226df7bfSsam random_num = (is->is_currnd = (is->is_currnd * 18741)-13849);
608226df7bfSsam random_num &= *pMask++;
609d5633a91Ssam *pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc);
610226df7bfSsam }
611226df7bfSsam }
612226df7bfSsam
613226df7bfSsam /*
614226df7bfSsam * Process an ioctl request.
615226df7bfSsam */
aceioctl(ifp,cmd,data)616226df7bfSsam aceioctl(ifp, cmd, data)
617226df7bfSsam register struct ifnet *ifp;
618226df7bfSsam int cmd;
619226df7bfSsam caddr_t data;
620226df7bfSsam {
621c0fcc33dSsam register struct ifaddr *ifa = (struct ifaddr *)data;
6227a0e3b47Ssam struct acedevice *addr;
623c0fcc33dSsam int s = splimp(), error = 0;
624226df7bfSsam
625226df7bfSsam switch (cmd) {
626226df7bfSsam
627226df7bfSsam case SIOCSIFADDR:
628c0fcc33dSsam ifp->if_flags |= IFF_UP;
6299d42a7deSsklower switch (ifa->ifa_addr->sa_family) {
6307a0e3b47Ssam #ifdef INET
6317a0e3b47Ssam case AF_INET:
6327a0e3b47Ssam aceinit(ifp->if_unit); /* before arpwhohas */
6337a0e3b47Ssam ((struct arpcom *)ifp)->ac_ipaddr =
6347a0e3b47Ssam IA_SIN(ifa)->sin_addr;
635c0fcc33dSsam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
636226df7bfSsam break;
6377a0e3b47Ssam #endif
6387a0e3b47Ssam #ifdef NS
6397a0e3b47Ssam case AF_NS: {
640e79104c5Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
641e79104c5Ssam struct ace_softc *is = &ace_softc[ifp->if_unit];
642226df7bfSsam
6437a0e3b47Ssam if (!ns_nullhost(*ina)) {
6447a0e3b47Ssam ifp->if_flags &= ~IFF_RUNNING;
6457a0e3b47Ssam addr = (struct acedevice *)
646e79104c5Ssam aceinfo[ifp->if_unit]->ui_addr;
647c0fcc33dSsam movow(&addr->csr, CSR_RESET);
648226df7bfSsam DELAY(10000);
6497a0e3b47Ssam /* set station address & copy addr to arp */
65030e6d859Ssam acesetaddr(ifp->if_unit, addr,
6517a0e3b47Ssam ina->x_host.c_host);
6527a0e3b47Ssam } else
653e79104c5Ssam ina->x_host = *(union ns_host *)is->is_addr;
6547a0e3b47Ssam aceinit(ifp->if_unit);
655226df7bfSsam break;
656226df7bfSsam }
657226df7bfSsam #endif
6587a0e3b47Ssam default:
6597a0e3b47Ssam aceinit(ifp->if_unit);
6607a0e3b47Ssam break;
6617a0e3b47Ssam }
6627a0e3b47Ssam break;
6637a0e3b47Ssam
6647a0e3b47Ssam case SIOCSIFFLAGS:
6657a0e3b47Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) {
6667a0e3b47Ssam addr = (struct acedevice *)
6677a0e3b47Ssam (aceinfo[ifp->if_unit]->ui_addr);
6687a0e3b47Ssam movow(&addr->csr, CSR_RESET);
6697a0e3b47Ssam ifp->if_flags &= ~IFF_RUNNING;
6707a0e3b47Ssam } else if (ifp->if_flags&IFF_UP &&
6717a0e3b47Ssam (ifp->if_flags&IFF_RUNNING) == 0)
6727a0e3b47Ssam aceinit(ifp->if_unit);
6737a0e3b47Ssam break;
674226df7bfSsam
675226df7bfSsam default:
676226df7bfSsam error = EINVAL;
677226df7bfSsam }
678226df7bfSsam splx(s);
679226df7bfSsam return (error);
680226df7bfSsam }
681226df7bfSsam
68230e6d859Ssam /*
68330e6d859Ssam * Set the on-board station address, then read it back
68430e6d859Ssam * to initialize the address used by ARP (among others).
68530e6d859Ssam */
acesetaddr(unit,addr,station)68630e6d859Ssam acesetaddr(unit, addr, station)
68730e6d859Ssam short unit;
68830e6d859Ssam struct acedevice *addr;
6891c94db25Ssam u_char *station;
69030e6d859Ssam {
69130e6d859Ssam struct ace_softc *is = &ace_softc[unit];
69230e6d859Ssam register short *wp, i;
69330e6d859Ssam
69430e6d859Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++)
69530e6d859Ssam movow(wp++, ~*station++);
69630e6d859Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++)
69730e6d859Ssam is->is_addr[i] = ~*wp++;
69830e6d859Ssam printf("ace%d: hardware address %s\n", unit,
69930e6d859Ssam ether_sprintf(is->is_addr));
70030e6d859Ssam }
70130e6d859Ssam
70230e6d859Ssam /*
70330e6d859Ssam * Setup the device for use. Initialize dual-ported memory,
70430e6d859Ssam * backoff parameters, and various other software state.
70530e6d859Ssam */
acesetup(unit)706a29de3d6Ssam acesetup(unit)
707226df7bfSsam int unit;
708226df7bfSsam {
709226df7bfSsam register struct ace_softc *is = &ace_softc[unit];
710226df7bfSsam register char *pData1;
711a29de3d6Ssam register short i;
712a29de3d6Ssam struct acedevice *addr;
713226df7bfSsam
714a29de3d6Ssam bzero(is->is_dpm, 16384*2);
715226df7bfSsam is->is_currnd = 49123;
716a29de3d6Ssam addr = (struct acedevice *)aceinfo[unit]->ui_addr;
717226df7bfSsam is->is_segboundry = (addr->segb >> 11) & 0xf;
718a29de3d6Ssam pData1 = is->is_dpm + (is->is_segboundry << 11);
719226df7bfSsam for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) {
720226df7bfSsam acebakoff(is, (struct tx_segment *)pData1, 15);
721226df7bfSsam pData1 += sizeof (struct tx_segment);
722226df7bfSsam }
723226df7bfSsam is->is_eictr = 0;
724226df7bfSsam is->is_eoctr = is->is_txnext = is->is_segboundry;
725226df7bfSsam bzero((char *)&is->is_stats, sizeof (is->is_stats));
726226df7bfSsam }
727226df7bfSsam #endif
728