xref: /original-bsd/sys/tahoe/if/if_ace.c (revision db0af71c)
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 = &top;
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