xref: /original-bsd/sys/tahoe/if/if_ace.c (revision c5249c6d)
1*c5249c6dSsam /*	if_ace.c	1.8	86/01/26	*/
2226df7bfSsam 
3226df7bfSsam /*
4226df7bfSsam  * ACC VERSAbus Ethernet controller
5226df7bfSsam  */
6226df7bfSsam #include "ace.h"
7226df7bfSsam #if NACE > 0
8226df7bfSsam 
9226df7bfSsam #include "../machine/pte.h"
10226df7bfSsam 
11c0fcc33dSsam #include "param.h"
12c0fcc33dSsam #include "systm.h"
13c0fcc33dSsam #include "mbuf.h"
14c0fcc33dSsam #include "buf.h"
15c0fcc33dSsam #include "protosw.h"
16c0fcc33dSsam #include "socket.h"
17c0fcc33dSsam #include "vmmac.h"
18c0fcc33dSsam #include "ioctl.h"
19c0fcc33dSsam #include "errno.h"
20c0fcc33dSsam #include "vmparam.h"
217a0e3b47Ssam #include "syslog.h"
22226df7bfSsam 
23226df7bfSsam #include "../net/if.h"
24226df7bfSsam #include "../net/netisr.h"
25226df7bfSsam #include "../net/route.h"
267a0e3b47Ssam #ifdef INET
27226df7bfSsam #include "../netinet/in.h"
28226df7bfSsam #include "../netinet/in_systm.h"
29c0fcc33dSsam #include "../netinet/in_var.h"
30226df7bfSsam #include "../netinet/ip.h"
31226df7bfSsam #include "../netinet/ip_var.h"
32226df7bfSsam #include "../netinet/if_ether.h"
337a0e3b47Ssam #endif
347a0e3b47Ssam #ifdef NS
357a0e3b47Ssam #include "../netns/ns.h"
367a0e3b47Ssam #include "../netns/ns_if.h"
377a0e3b47Ssam #endif
38226df7bfSsam 
39226df7bfSsam #include "../tahoe/mtpr.h"
40226df7bfSsam #include "../tahoeif/if_acereg.h"
41c0fcc33dSsam #include "../tahoevba/vbavar.h"
42226df7bfSsam 
43226df7bfSsam /* station address */
44226df7bfSsam char	ace_station[6] = { ~0x8, ~0x0, ~0x3, ~0x0, ~0x0, ~0x1 };
45226df7bfSsam /* multicast hash table initializer */
46226df7bfSsam char	ace_hash[8] = { ~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF };
47226df7bfSsam /* backoff table masks */
48226df7bfSsam short random_mask_tbl[16] = {
49226df7bfSsam 	0x0040, 0x00C0, 0x01C0, 0x03C0, 0x07C0, 0x0FC0, 0x1FC0, 0x3FC0,
50226df7bfSsam 	0x7FC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0
51226df7bfSsam };
52226df7bfSsam 
53226df7bfSsam int	aceprobe(), aceattach(), acerint(), acecint();
54226df7bfSsam struct	vba_device *aceinfo[NACE];
55*c5249c6dSsam long	acestd[] = { 0 };
56226df7bfSsam struct	vba_driver acedriver =
57226df7bfSsam     { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 };
58226df7bfSsam 
59226df7bfSsam int	aceinit(), aceoutput(), aceioctl(), acereset();
60226df7bfSsam struct	mbuf *aceget();
61226df7bfSsam 
62226df7bfSsam /*
63226df7bfSsam  * Ethernet software status per interface.
64226df7bfSsam  *
65226df7bfSsam  * Each interface is referenced by a network interface structure,
66226df7bfSsam  * is_if, which the routing code uses to locate the interface.
67226df7bfSsam  * This structure contains the output queue for the interface, its address, ...
68226df7bfSsam  */
69226df7bfSsam struct	ace_softc {
70226df7bfSsam 	struct	arpcom is_ac;		/* Ethernet common part	*/
71226df7bfSsam #define	is_if	is_ac.ac_if		/* network-visible interface */
72226df7bfSsam #define	is_addr	is_ac.ac_enaddr		/* hardware Ethernet address */
73226df7bfSsam 	short	is_flags;
74226df7bfSsam #define	ACEF_OACTIVE	0x1		/* output is active */
75226df7bfSsam #define	ACEF_RCVPENDING	0x2		/* start rcv in acecint	*/
76226df7bfSsam 	short	is_promiscuous;		/* true is enabled */
77226df7bfSsam 	short	is_segboundry;		/* first TX Seg in dpm */
78226df7bfSsam 	short	is_eictr;		/* Rx segment tracking ctr */
79226df7bfSsam 	short	is_eoctr;		/* Tx segment tracking ctr */
80226df7bfSsam 	short	is_txnext;		/* Next available Tx segment */
81226df7bfSsam 	short	is_currnd;		/* current random backoff */
82226df7bfSsam 	struct	ace_stats is_stats;	/* holds board statistics */
83226df7bfSsam 	short	is_xcnt;		/* count xmitted segments to be acked
84226df7bfSsam 					   by the controller */
857a0e3b47Ssam 	long	is_ivec;		/* autoconfig interrupt vector base */
86a29de3d6Ssam 	struct	pte *is_map;		/* pte map for dual ported memory */
87a29de3d6Ssam 	caddr_t	is_dpm;			/* address of mapped memory */
88226df7bfSsam } ace_softc[NACE];
89226df7bfSsam extern	struct ifnet loif;
90226df7bfSsam 
917a0e3b47Ssam aceprobe(reg, vi)
92226df7bfSsam 	caddr_t reg;
937a0e3b47Ssam 	struct vba_device *vi;
94226df7bfSsam {
957a0e3b47Ssam 	register br, cvec;		/* must be r12, r11 */
967a0e3b47Ssam 	struct acedevice *ap = (struct acedevice *)reg;
977a0e3b47Ssam 	struct ace_softc *is = &ace_softc[vi->ui_unit];
98226df7bfSsam 
99226df7bfSsam #ifdef lint
100226df7bfSsam 	acerint(0); acecint(0);
101226df7bfSsam #endif
102226df7bfSsam 	if (badaddr(reg, 2))
103226df7bfSsam 		return (0);
1047a0e3b47Ssam 	movow(&ap->csr, CSR_RESET);
105226df7bfSsam 	DELAY(10000);
1067a0e3b47Ssam #ifdef notdef
1077a0e3b47Ssam 	/*
1087a0e3b47Ssam 	 * Select two spaces for the interrupts aligned to an
1097a0e3b47Ssam 	 * eight vector boundary and fitting in 8 bits (as
1107a0e3b47Ssam 	 * required by the controller) -- YECH.  The controller
1117a0e3b47Ssam 	 * will be notified later at initialization time.
1127a0e3b47Ssam 	 */
1137a0e3b47Ssam 	if ((vi->ui_hd->vh_lastiv -= 2) > 0xff)
1147a0e3b47Ssam 		vi->ui_hd->vh_lastiv  = 0x200;
1157a0e3b47Ssam 	is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7;
1167a0e3b47Ssam #else
1177a0e3b47Ssam 	is->is_ivec = 0x90+vi->ui_unit*8;
1187a0e3b47Ssam #endif
1197a0e3b47Ssam 	br = 0x14, cvec = is->is_ivec;		/* XXX */
1207a0e3b47Ssam 	return (sizeof (*ap));
121226df7bfSsam }
122226df7bfSsam 
123226df7bfSsam /*
124226df7bfSsam  * Interface exists: make available by filling in network interface
125226df7bfSsam  * record.  System will initialize the interface when it is ready
126226df7bfSsam  * to accept packets.
127226df7bfSsam  */
128226df7bfSsam aceattach(ui)
129226df7bfSsam 	struct vba_device *ui;
130226df7bfSsam {
131226df7bfSsam 	register short unit = ui->ui_unit;
132226df7bfSsam 	register struct ace_softc *is = &ace_softc[unit];
133226df7bfSsam 	register struct ifnet *ifp = &is->is_if;
134226df7bfSsam 	register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
135226df7bfSsam 	register short *wp, i;
136226df7bfSsam 
137226df7bfSsam 	ifp->if_unit = unit;
138226df7bfSsam 	ifp->if_name = "ace";
139226df7bfSsam 	ifp->if_mtu = ETHERMTU;
140226df7bfSsam 	/*
141a29de3d6Ssam 	 * Set station's addresses and multicast hash table.
142226df7bfSsam 	 */
143226df7bfSsam 	ace_station[5] = ~(unit + 1);
144226df7bfSsam 	acesetetaddr(unit, addr, ace_station);
145226df7bfSsam 	is->is_promiscuous = 0;
146226df7bfSsam 	wp = (short *)addr->hash;
147226df7bfSsam 	for (i =  0; i < 8; i++)
148c0fcc33dSsam 		movow(wp++, ace_hash[i]);
149c0fcc33dSsam 	movow(&addr->bcastena[0], ~0xffff);
150c0fcc33dSsam 	movow(&addr->bcastena[1], ~0xffff);
151a29de3d6Ssam 	/*
152a29de3d6Ssam 	 * Allocate and map dual ported VERSAbus memory.
153a29de3d6Ssam 	 */
154a29de3d6Ssam 	vbmemalloc(32, (caddr_t)ui->ui_flags, &is->is_map, &is->is_dpm);
155a29de3d6Ssam 
156226df7bfSsam 	ifp->if_init = aceinit;
157226df7bfSsam 	ifp->if_output = aceoutput;
158226df7bfSsam 	ifp->if_ioctl = aceioctl;
159226df7bfSsam 	ifp->if_reset = acereset;
160c0fcc33dSsam 	ifp->if_flags = IFF_BROADCAST;
161226df7bfSsam 	if_attach(ifp);
162226df7bfSsam }
163226df7bfSsam 
164226df7bfSsam acesetetaddr(unit, addr, station_addr)
165226df7bfSsam 	short unit;
166226df7bfSsam 	struct acedevice *addr;
167226df7bfSsam 	char *station_addr;
168226df7bfSsam {
169226df7bfSsam 	register short *wp, i;
170226df7bfSsam 	register char *cp;
171226df7bfSsam 	struct ace_softc *is = &ace_softc[unit];
172226df7bfSsam 
173226df7bfSsam 	wp = (short *)addr->station;
174226df7bfSsam 	cp = station_addr;
175226df7bfSsam 	for (i = 0; i < 6; i++)
176c0fcc33dSsam 		movow(wp++, *cp++);
177226df7bfSsam 	wp = (short *)addr->station;
178c0fcc33dSsam 	cp = (char *)is->is_addr;
179226df7bfSsam 	for (i = 0; i < 6; i++)
180226df7bfSsam 		*cp++ = ~(*wp++);
181226df7bfSsam }
182226df7bfSsam 
183226df7bfSsam /*
184226df7bfSsam  * Reset of interface after "system" reset.
185226df7bfSsam  */
186226df7bfSsam acereset(unit, vban)
187226df7bfSsam 	int unit, vban;
188226df7bfSsam {
189226df7bfSsam 	register struct vba_device *ui;
190226df7bfSsam 
191226df7bfSsam 	if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 ||
192226df7bfSsam 	    ui->ui_vbanum != vban)
193226df7bfSsam 		return;
194226df7bfSsam 	printf(" ace%d", unit);
195226df7bfSsam 	aceinit(unit);
196226df7bfSsam }
197226df7bfSsam 
198226df7bfSsam /*
199226df7bfSsam  * Initialization of interface; clear recorded pending operations
200226df7bfSsam  */
201226df7bfSsam aceinit(unit)
202226df7bfSsam 	int unit;
203226df7bfSsam {
204226df7bfSsam 	register struct ace_softc *is = &ace_softc[unit];
205226df7bfSsam 	register struct vba_device *ui = aceinfo[unit];
206226df7bfSsam 	register struct acedevice *addr;
207226df7bfSsam 	register struct ifnet *ifp = &is->is_if;
208226df7bfSsam 	register short Csr;
209c0fcc33dSsam 	register int s;
210226df7bfSsam 
211c0fcc33dSsam 	if (ifp->if_addrlist == (struct ifaddr *)0)
212226df7bfSsam 		return;
213226df7bfSsam 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
214226df7bfSsam 		/*
215226df7bfSsam 		 * Reset the controller, initialize the recieve buffers,
216226df7bfSsam 		 * and turn the controller on again and set board online.
217226df7bfSsam 		 */
218226df7bfSsam 		addr = (struct acedevice *)ui->ui_addr;
219226df7bfSsam 		s = splimp();
220c0fcc33dSsam 		movow(&addr->csr, CSR_RESET);
221226df7bfSsam 		DELAY(10000);
222226df7bfSsam 
223226df7bfSsam 		/*
224a29de3d6Ssam 		 * Clean up dpm since the controller might
225a29de3d6Ssam 		 * jumble dpm after reset.
226226df7bfSsam 		 */
227a29de3d6Ssam 		acesetup(unit);
228c0fcc33dSsam 		movow(&addr->csr, CSR_GO);
229226df7bfSsam 		Csr = addr->csr;
230226df7bfSsam 		if (Csr & CSR_ACTIVE) {
2317a0e3b47Ssam 			movow(&addr->ivct, is->is_ivec);
232226df7bfSsam 			Csr |= CSR_IENA | is->is_promiscuous;
233c0fcc33dSsam 			movow(&addr->csr, Csr);
234226df7bfSsam 			is->is_flags = 0;
235226df7bfSsam 			is->is_xcnt = 0;
236c0fcc33dSsam 			is->is_if.if_flags |= IFF_RUNNING;
237226df7bfSsam 		}
238226df7bfSsam 		splx(s);
239226df7bfSsam 	}
240c0fcc33dSsam 	if (is->is_if.if_snd.ifq_head)
241a29de3d6Ssam 		acestart(unit);
242226df7bfSsam }
243226df7bfSsam 
244226df7bfSsam /*
245226df7bfSsam  * Start output on interface.
246226df7bfSsam  * Get another datagram to send off of the interface queue,
247226df7bfSsam  * and map it to the interface before starting the output.
248226df7bfSsam  *
249226df7bfSsam  */
250a29de3d6Ssam acestart(unit)
251a29de3d6Ssam 	int unit;
252226df7bfSsam {
253226df7bfSsam 	register struct tx_segment *txs;
254c0fcc33dSsam 	register long len;
255c0fcc33dSsam 	register int s;
256226df7bfSsam 	register struct ace_softc *is = &ace_softc[unit];
257226df7bfSsam 	struct mbuf *m;
258c0fcc33dSsam 	short retries;
259226df7bfSsam 
260a29de3d6Ssam 	if (is->is_flags & ACEF_OACTIVE)
261a29de3d6Ssam 		return;
262a29de3d6Ssam 	is->is_flags |= ACEF_OACTIVE;
263226df7bfSsam again:
264226df7bfSsam 	txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11));
265226df7bfSsam 	if (txs->tx_csr & TCS_TBFULL) {
266226df7bfSsam 		is->is_stats.tx_busy++;
267a29de3d6Ssam 		is->is_flags &= ~ACEF_OACTIVE;
268226df7bfSsam 		return;
269226df7bfSsam 	}
270c0fcc33dSsam 	s = splimp();
271226df7bfSsam 	IF_DEQUEUE(&is->is_if.if_snd, m);
272c0fcc33dSsam 	splx(s);
273a29de3d6Ssam 	if (m == 0) {
274a29de3d6Ssam 		is->is_flags &= ~ACEF_OACTIVE;
275226df7bfSsam 		return;
276a29de3d6Ssam 	}
277226df7bfSsam 	len = aceput(unit, txs->tx_data, m);
278226df7bfSsam 	retries = txs->tx_csr & TCS_RTC;
279226df7bfSsam 	if (retries > 0)
280226df7bfSsam 		acebakoff(is, txs, retries);
281226df7bfSsam 
282226df7bfSsam 	/*
283226df7bfSsam 	 * Ensure minimum packet length.
284226df7bfSsam 	 * This makes the safe assumtion that there are no virtual holes
285226df7bfSsam 	 * after the data.
286226df7bfSsam 	 * For security, it might be wise to zero out the added bytes,
287226df7bfSsam 	 * but we're mainly interested in speed at the moment.
288226df7bfSsam 	 */
289226df7bfSsam 	if (len - sizeof (struct ether_header) < ETHERMIN)
290226df7bfSsam 		len = ETHERMIN + sizeof (struct ether_header);
291226df7bfSsam 	if (++is->is_txnext > SEG_MAX)
292226df7bfSsam 		is->is_txnext = is->is_segboundry;
293226df7bfSsam 	is->is_if.if_opackets++;
294226df7bfSsam 	is->is_xcnt++;
295226df7bfSsam 	len = (len & 0x7fff) | TCS_TBFULL;
296c0fcc33dSsam 	movow(txs, len);
297226df7bfSsam 	goto again;
298226df7bfSsam }
299226df7bfSsam 
300226df7bfSsam /*
301226df7bfSsam  * Transmit done interrupt.
302226df7bfSsam  */
303226df7bfSsam acecint(unit)
304226df7bfSsam 	int unit;
305226df7bfSsam {
306226df7bfSsam 	register struct ace_softc *is = &ace_softc[unit];
307226df7bfSsam 	register struct tx_segment *txseg;
308c0fcc33dSsam 	short eostat;
309226df7bfSsam 
310226df7bfSsam 	if (is->is_xcnt <= 0)  {
3117a0e3b47Ssam 		log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n",
312226df7bfSsam 		    unit, is->is_xcnt);
313226df7bfSsam 		is->is_xcnt = 0;
314c0fcc33dSsam 		if (is->is_if.if_snd.ifq_head)
315a29de3d6Ssam 			acestart(unit);
316226df7bfSsam 		return;
317226df7bfSsam 	}
318226df7bfSsam 	is->is_xcnt--;
319226df7bfSsam 	txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm);
320226df7bfSsam 	eostat = txseg->tx_csr;
321226df7bfSsam 	if ((eostat & TCS_TBFULL) == 0) {
322226df7bfSsam 		is->is_stats.tx_retries += eostat & TCS_RTC;
323226df7bfSsam 		if (eostat & TCS_RTFAIL)  {
324226df7bfSsam 			is->is_stats.tx_discarded++;
325226df7bfSsam 			is->is_if.if_oerrors++;
326226df7bfSsam 		} else
327226df7bfSsam 			is->is_stats.tx_datagrams++;
328226df7bfSsam 		if (++is->is_eoctr >= 16)
329226df7bfSsam 			is->is_eoctr = is->is_segboundry;
330226df7bfSsam 	}
331c0fcc33dSsam 	if (is->is_if.if_snd.ifq_head)
332a29de3d6Ssam 		acestart(unit);
333226df7bfSsam }
334226df7bfSsam 
335226df7bfSsam /*
336226df7bfSsam  * Ethernet interface receiver interrupt.
337226df7bfSsam  * If input error just drop packet.
338226df7bfSsam  * Otherwise purge input buffered data path and examine
339226df7bfSsam  * packet to determine type.  If can't determine length
340226df7bfSsam  * from type, then have to drop packet.  Othewise decapsulate
341226df7bfSsam  * packet based on type and pass to type specific higher-level
342226df7bfSsam  * input routine.
343226df7bfSsam  */
344226df7bfSsam acerint(unit)
345226df7bfSsam 	int unit;
346226df7bfSsam {
347226df7bfSsam 	register struct ace_softc *is = &ace_softc[unit];
348226df7bfSsam 	register struct ifqueue *inq;
349226df7bfSsam 	register struct ether_header *ace;
350226df7bfSsam 	register struct rx_segment *rxseg;
351226df7bfSsam 	int len, s, off, resid;
352226df7bfSsam 	struct mbuf *m;
353226df7bfSsam 	short eistat;
354226df7bfSsam 
355c0fcc33dSsam 	if ((is->is_if.if_flags&IFF_RUNNING) == 0)
356c0fcc33dSsam 		return;
357226df7bfSsam again:
358226df7bfSsam 	rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm);
359226df7bfSsam 	eistat = rxseg->rx_csr;
360226df7bfSsam 	if ((eistat & RCS_RBFULL) == 0)
361226df7bfSsam 		return;
362226df7bfSsam 	is->is_if.if_ipackets++;
363226df7bfSsam 	if (++is->is_eictr >= is->is_segboundry)
364226df7bfSsam 		is->is_eictr = 0;
365226df7bfSsam 	len = eistat & RCS_RBC;
366226df7bfSsam 	if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) ||
367226df7bfSsam 	    len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) {
368226df7bfSsam 		if (eistat & RCS_ROVRN)
369226df7bfSsam 			is->is_stats.rx_overruns++;
370226df7bfSsam 		if (eistat & RCS_RCRC)
371226df7bfSsam 			is->is_stats.rx_crc_errors++;
372226df7bfSsam 		if (eistat & RCS_RODD)
373226df7bfSsam 			is->is_stats.rx_align_errors++;
374226df7bfSsam 		if (len < ET_MINLEN)
375226df7bfSsam 			is->is_stats.rx_underruns++;
376226df7bfSsam 		if (len > ET_MAXLEN+CRC_SIZE)
377226df7bfSsam 			is->is_stats.rx_overruns++;
378226df7bfSsam 		is->is_if.if_ierrors++;
379226df7bfSsam 		rxseg->rx_csr = 0;
380226df7bfSsam 		return;
381226df7bfSsam 	} else
382226df7bfSsam 		is->is_stats.rx_datagrams++;
383226df7bfSsam 	ace = (struct ether_header *)rxseg->rx_data;
384226df7bfSsam 	len -= sizeof (struct ether_header);
385226df7bfSsam 	/*
386c0fcc33dSsam 	 * Deal with trailer protocol: if type is trailer
387226df7bfSsam 	 * get true type from first 16-bit word past data.
388226df7bfSsam 	 * Remember that type was trailer by setting off.
389226df7bfSsam 	 */
390226df7bfSsam 	ace->ether_type = ntohs((u_short)ace->ether_type);
391226df7bfSsam #define	acedataaddr(ace, off, type) \
392226df7bfSsam     ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off))))
393c0fcc33dSsam 	if (ace->ether_type >= ETHERTYPE_TRAIL &&
394c0fcc33dSsam 	    ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
395c0fcc33dSsam 		off = (ace->ether_type - ETHERTYPE_TRAIL) * 512;
396226df7bfSsam 		if (off >= ETHERMTU)
397226df7bfSsam 			goto setup;		/* sanity */
398226df7bfSsam 		ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *));
399226df7bfSsam 		resid = ntohs(*(acedataaddr(ace, off+2, u_short *)));
400226df7bfSsam 		if (off + resid > len)
401226df7bfSsam 			goto setup;		/* sanity */
402226df7bfSsam 		len = off + resid;
403226df7bfSsam 	} else
404226df7bfSsam 		off = 0;
405226df7bfSsam 	if (len == 0)
406226df7bfSsam 		goto setup;
407226df7bfSsam 
408226df7bfSsam 	/*
409226df7bfSsam 	 * Pull packet off interface.  Off is nonzero if packet
410226df7bfSsam 	 * has trailing header; aceget will then force this header
411226df7bfSsam 	 * information to be at the front, but we still have to drop
412226df7bfSsam 	 * the type and length which are at the front of any trailer data.
413226df7bfSsam 	 */
4147a0e3b47Ssam 	m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if);
415226df7bfSsam 	if (m == 0)
416226df7bfSsam 		goto setup;
417226df7bfSsam 	if (off) {
4187a0e3b47Ssam 		struct ifnet *ifp;
4197a0e3b47Ssam 
4207a0e3b47Ssam 		ifp = *(mtod(m, struct ifnet **));
421226df7bfSsam 		m->m_off += 2 * sizeof (u_short);
422226df7bfSsam 		m->m_len -= 2 * sizeof (u_short);
4237a0e3b47Ssam 		*(mtod(m, struct ifnet **)) = ifp;
424226df7bfSsam 	}
425226df7bfSsam 	switch (ace->ether_type) {
426226df7bfSsam 
427226df7bfSsam #ifdef INET
428c0fcc33dSsam 	case ETHERTYPE_IP:
429226df7bfSsam 		schednetisr(NETISR_IP);
430226df7bfSsam 		inq = &ipintrq;
431226df7bfSsam 		break;
4327a0e3b47Ssam #endif
433226df7bfSsam 
434c0fcc33dSsam 	case ETHERTYPE_ARP:
435226df7bfSsam 		arpinput(&is->is_ac, m);
436226df7bfSsam 		goto setup;
437c0fcc33dSsam #ifdef NS
438c0fcc33dSsam 	case ETHERTYPE_NS:
439c0fcc33dSsam 		schednetisr(NETISR_NS);
440c0fcc33dSsam 		inq = &nsintrq;
441c0fcc33dSsam 		break;
442c0fcc33dSsam 
443c0fcc33dSsam #endif
444226df7bfSsam 	default:
445226df7bfSsam 		m_freem(m);
446226df7bfSsam 		goto setup;
447226df7bfSsam 	}
448226df7bfSsam 	if (IF_QFULL(inq)) {
449226df7bfSsam 		IF_DROP(inq);
450226df7bfSsam 		m_freem(m);
451226df7bfSsam 		goto setup;
452226df7bfSsam 	}
453226df7bfSsam 	s = splimp();
454226df7bfSsam 	IF_ENQUEUE(inq, m);
455226df7bfSsam 	splx(s);
456226df7bfSsam setup:
457226df7bfSsam 	rxseg->rx_csr = 0;
458226df7bfSsam 	goto again;
459226df7bfSsam }
460226df7bfSsam 
461226df7bfSsam /*
462226df7bfSsam  * Ethernet output routine.
463226df7bfSsam  * Encapsulate a packet of type family for the local net.
464226df7bfSsam  * Use trailer local net encapsulation if enough data in first
465226df7bfSsam  * packet leaves a multiple of 512 bytes of data in remainder.
466226df7bfSsam  */
467226df7bfSsam aceoutput(ifp, m0, dst)
468226df7bfSsam 	struct ifnet *ifp;
469226df7bfSsam 	struct mbuf *m0;
470226df7bfSsam 	struct sockaddr *dst;
471226df7bfSsam {
472226df7bfSsam 	register struct ace_softc *is = &ace_softc[ifp->if_unit];
473226df7bfSsam 	register struct mbuf *m = m0;
474226df7bfSsam 	register struct ether_header *ace;
475226df7bfSsam 	register int off;
476226df7bfSsam 	struct mbuf *mcopy = (struct mbuf *)0;
477f814bab2Ssam 	int type, s, error, usetrailers;
478c0fcc33dSsam 	u_char edst[6];
479226df7bfSsam 	struct in_addr idst;
480226df7bfSsam 
4817a0e3b47Ssam 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
4827a0e3b47Ssam 		error = ENETDOWN;
4837a0e3b47Ssam 		goto bad;
4847a0e3b47Ssam 	}
485226df7bfSsam 	switch (dst->sa_family) {
486226df7bfSsam 
487226df7bfSsam #ifdef INET
488226df7bfSsam 	case AF_INET:
489226df7bfSsam 		idst = ((struct sockaddr_in *)dst)->sin_addr;
490f814bab2Ssam 		if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers))
491226df7bfSsam 			return (0);	/* if not yet resolved */
492c0fcc33dSsam 		if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr,
493c0fcc33dSsam 		    sizeof (edst)))
494226df7bfSsam 			mcopy = m_copy(m, 0, (int)M_COPYALL);
495226df7bfSsam 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
496226df7bfSsam 		/* need per host negotiation */
497f814bab2Ssam 		if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
498226df7bfSsam 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
499c0fcc33dSsam 			type = ETHERTYPE_TRAIL + (off>>9);
500226df7bfSsam 			m->m_off -= 2 * sizeof (u_short);
501226df7bfSsam 			m->m_len += 2 * sizeof (u_short);
502c0fcc33dSsam 			*mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
503226df7bfSsam 			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
504226df7bfSsam 			goto gottrailertype;
505226df7bfSsam 		}
506c0fcc33dSsam 		type = ETHERTYPE_IP;
507c0fcc33dSsam 		off = 0;
508c0fcc33dSsam 		goto gottype;
509c0fcc33dSsam #endif
510c0fcc33dSsam #ifdef NS
511c0fcc33dSsam 	case AF_NS:
512c0fcc33dSsam  		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
513c0fcc33dSsam 		    (caddr_t)edst, sizeof (edst));
514c0fcc33dSsam 		if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst)))
515c0fcc33dSsam 			mcopy = m_copy(m, 0, (int)M_COPYALL);
516c0fcc33dSsam 		else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost,
517c0fcc33dSsam 		    sizeof(edst)))
518c0fcc33dSsam 			return(looutput(&loif, m, dst));
519c0fcc33dSsam 		type = ETHERTYPE_NS;
520226df7bfSsam 		off = 0;
521226df7bfSsam 		goto gottype;
522226df7bfSsam #endif
523226df7bfSsam 	case AF_UNSPEC:
524226df7bfSsam 		ace = (struct ether_header *)dst->sa_data;
525c0fcc33dSsam 		bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst));
526226df7bfSsam 		type = ace->ether_type;
527226df7bfSsam 		goto gottype;
528226df7bfSsam 
529226df7bfSsam 	default:
5307a0e3b47Ssam 		log(LOG_ERR, "ace%d: can't handle af%d\n",
531226df7bfSsam 		    ifp->if_unit, dst->sa_family);
532226df7bfSsam 		error = EAFNOSUPPORT;
533226df7bfSsam 		goto bad;
534226df7bfSsam 	}
535226df7bfSsam 
536226df7bfSsam gottrailertype:
537226df7bfSsam 	/*
538226df7bfSsam 	 * Packet to be sent as trailer: move first packet
539226df7bfSsam 	 * (control information) to end of chain.
540226df7bfSsam 	 */
541226df7bfSsam 	while (m->m_next)
542226df7bfSsam 		m = m->m_next;
543226df7bfSsam 	m->m_next = m0;
544226df7bfSsam 	m = m0->m_next;
545226df7bfSsam 	m0->m_next = 0;
546226df7bfSsam 	m0 = m;
547226df7bfSsam 
548226df7bfSsam gottype:
549226df7bfSsam 	/*
550226df7bfSsam 	 * Add local net header.  If no space in first mbuf,
551226df7bfSsam 	 * allocate another.
552226df7bfSsam 	 */
553226df7bfSsam 	if (m->m_off > MMAXOFF ||
554226df7bfSsam 	    MMINOFF + sizeof (struct ether_header) > m->m_off) {
555226df7bfSsam 		m = m_get(M_DONTWAIT, MT_HEADER);
556226df7bfSsam 		if (m == 0) {
557226df7bfSsam 			error = ENOBUFS;
558226df7bfSsam 			goto bad;
559226df7bfSsam 		}
560226df7bfSsam 		m->m_next = m0;
561226df7bfSsam 		m->m_off = MMINOFF;
562226df7bfSsam 		m->m_len = sizeof (struct ether_header);
563226df7bfSsam 	} else {
564226df7bfSsam 		m->m_off -= sizeof (struct ether_header);
565226df7bfSsam 		m->m_len += sizeof (struct ether_header);
566226df7bfSsam 	}
567226df7bfSsam 	ace = mtod(m, struct ether_header *);
568c0fcc33dSsam 	bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst));
569c0fcc33dSsam 	bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost,
570c0fcc33dSsam 	    sizeof (is->is_addr));
571226df7bfSsam 	ace->ether_type = htons((u_short)type);
572226df7bfSsam 
573226df7bfSsam 	/*
574226df7bfSsam 	 * Queue message on interface, and start output if interface
575226df7bfSsam 	 * not yet active.
576226df7bfSsam 	 */
577226df7bfSsam 	s = splimp();
578226df7bfSsam 	if (IF_QFULL(&ifp->if_snd)) {
579226df7bfSsam 		IF_DROP(&ifp->if_snd);
580226df7bfSsam 		error = ENOBUFS;
581226df7bfSsam 		goto qfull;
582226df7bfSsam 	}
583226df7bfSsam 	IF_ENQUEUE(&ifp->if_snd, m);
584226df7bfSsam 	splx(s);
585a29de3d6Ssam 	acestart(ifp->if_unit);
586226df7bfSsam 	return (mcopy ? looutput(&loif, mcopy, dst) : 0);
587226df7bfSsam qfull:
588226df7bfSsam 	m0 = m;
589226df7bfSsam 	splx(s);
590226df7bfSsam bad:
591226df7bfSsam 	m_freem(m0);
592226df7bfSsam 	if (mcopy)
593226df7bfSsam 		m_freem(mcopy);
594226df7bfSsam 	return (error);
595226df7bfSsam }
596226df7bfSsam 
597226df7bfSsam /*
598226df7bfSsam  * Routine to copy from mbuf chain to transmit buffer on the VERSAbus
599226df7bfSsam  * If packet size is less than the minimum legal size,
600226df7bfSsam  * the buffer is expanded.  We probably should zero out the extra
601226df7bfSsam  * bytes for security, but that would slow things down.
602226df7bfSsam  */
603c0fcc33dSsam /*ARGSUSED*/
604226df7bfSsam aceput(unit, txbuf, m)
6057a0e3b47Ssam 	int unit;
606c0fcc33dSsam 	char *txbuf;
607226df7bfSsam 	struct mbuf *m;
608226df7bfSsam {
6097a0e3b47Ssam 	register u_char *bp, *mcp;
6107a0e3b47Ssam 	register short *s1, *s2;
611c0fcc33dSsam 	register u_int len;
612226df7bfSsam 	register struct mbuf *mp;
613c0fcc33dSsam 	int total;
614226df7bfSsam 
615226df7bfSsam 	total = 0;
616c0fcc33dSsam 	bp = (u_char *)txbuf;
617226df7bfSsam 	for (mp = m; (mp); mp = mp->m_next) {
618226df7bfSsam 		len = mp->m_len;
619226df7bfSsam 		if (len == 0)
620226df7bfSsam 			continue;
621226df7bfSsam 		total += len;
622226df7bfSsam 		mcp = mtod(mp, u_char *);
623226df7bfSsam 		if (((int)mcp & 01) && ((int)bp & 01)) {
624226df7bfSsam 			/* source & destination at odd addresses */
625c0fcc33dSsam 			movob(bp++, *mcp++);
626226df7bfSsam 			--len;
627226df7bfSsam 		}
628226df7bfSsam 		if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) {
629c0fcc33dSsam 			register u_int l;
630226df7bfSsam 
631226df7bfSsam 			s1 = (short *)bp;
632226df7bfSsam 			s2 = (short *)mcp;
633226df7bfSsam 			l = len >> 1;		/* count # of shorts */
634c0fcc33dSsam 			while (l-- != 0)
635c0fcc33dSsam 				movow(s1++, *s2++);
636226df7bfSsam 			len &= 1;		/* # remaining bytes */
637226df7bfSsam 			bp = (u_char *)s1;
638226df7bfSsam 			mcp = (u_char *)s2;
639226df7bfSsam 		}
640c0fcc33dSsam 		while (len-- != 0)
641c0fcc33dSsam 			movob(bp++, *mcp++);
642226df7bfSsam 	}
643226df7bfSsam 	m_freem(m);
644226df7bfSsam 	return (total);
645226df7bfSsam }
646226df7bfSsam 
647226df7bfSsam /*
648226df7bfSsam  * Routine to copy from VERSAbus memory into mbufs.
649226df7bfSsam  *
650226df7bfSsam  * Warning: This makes the fairly safe assumption that
651226df7bfSsam  * mbufs have even lengths.
652226df7bfSsam  */
653c0fcc33dSsam /*ARGSUSED*/
654226df7bfSsam struct mbuf *
6557a0e3b47Ssam aceget(rxbuf, totlen, off0, ifp)
656226df7bfSsam 	u_char *rxbuf;
657226df7bfSsam 	int totlen, off0;
6587a0e3b47Ssam 	struct ifnet *ifp;
659226df7bfSsam {
6607a0e3b47Ssam 	register u_char *cp, *mcp;
661226df7bfSsam 	register int tlen;
662226df7bfSsam 	register struct mbuf *m;
663226df7bfSsam 	struct mbuf *top = 0, **mp = &top;
664226df7bfSsam 	int len, off = off0;
665226df7bfSsam 
666226df7bfSsam 	cp = rxbuf + sizeof (struct ether_header);
667226df7bfSsam 	while (totlen > 0) {
668226df7bfSsam 		MGET(m, M_DONTWAIT, MT_DATA);
669226df7bfSsam 		if (m == 0)
670226df7bfSsam 			goto bad;
671226df7bfSsam 		if (off) {
672226df7bfSsam 			len = totlen - off;
673226df7bfSsam 			cp = rxbuf + sizeof (struct ether_header) + off;
674226df7bfSsam 		} else
675226df7bfSsam 			len = totlen;
6767a0e3b47Ssam 		if (ifp)
6777a0e3b47Ssam 			len += sizeof(ifp);
678226df7bfSsam 		if (len >= CLBYTES) {
679226df7bfSsam 			struct mbuf *p;
680226df7bfSsam 
681226df7bfSsam 			MCLGET(p, 1);
682226df7bfSsam 			if (p != 0) {
683226df7bfSsam 				m->m_len = len = CLBYTES;
684226df7bfSsam 				m->m_off = (int)p - (int)m;
685226df7bfSsam 			} else {
686226df7bfSsam 				m->m_len = len = MIN(MLEN, len);
687226df7bfSsam 				m->m_off = MMINOFF;
688226df7bfSsam 			}
689226df7bfSsam 		} else {
690226df7bfSsam 			m->m_len = len = MIN(MLEN, len);
691226df7bfSsam 			m->m_off = MMINOFF;
692226df7bfSsam 		}
693226df7bfSsam 		mcp = mtod(m, u_char *);
6947a0e3b47Ssam 		if (ifp) {
6957a0e3b47Ssam 			/*
6967a0e3b47Ssam 			 * Prepend interface pointer to first mbuf.
6977a0e3b47Ssam 			 */
6987a0e3b47Ssam 			*(mtod(m, struct ifnet **)) = ifp;
6997a0e3b47Ssam 			mcp += sizeof(ifp);
7007a0e3b47Ssam 			len -= sizeof(ifp);
7017a0e3b47Ssam 			ifp = (struct ifnet *)0;
7027a0e3b47Ssam 		}
703226df7bfSsam 		/*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/
704226df7bfSsam 		/*cp += len; mcp += len;*/
705226df7bfSsam 		tlen = len;
706226df7bfSsam 		if (((int)mcp & 01) && ((int)cp & 01)) {
707226df7bfSsam 			/* source & destination at odd addresses */
708226df7bfSsam 			*mcp++ = *cp++;
709226df7bfSsam 			--tlen;
710226df7bfSsam 		}
711226df7bfSsam 		if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) {
712226df7bfSsam 			register short *s1, *s2;
713226df7bfSsam 			register int l;
714226df7bfSsam 
715226df7bfSsam 			s1 = (short *)mcp;
716226df7bfSsam 			s2 = (short *)cp;
717226df7bfSsam 			l = tlen >> 1;		/* count # of shorts */
718226df7bfSsam 			while (l-- > 0)		/* copy shorts */
719226df7bfSsam 				*s1++ = *s2++;
720226df7bfSsam 			tlen &= 1;		/* # remaining bytes */
721226df7bfSsam 			mcp = (u_char *)s1;
722226df7bfSsam 			cp = (u_char *)s2;
723226df7bfSsam 		}
724226df7bfSsam 		while (tlen-- > 0)
725226df7bfSsam 			*mcp++ = *cp++;
726226df7bfSsam 		*mp = m;
727226df7bfSsam 		mp = &m->m_next;
728226df7bfSsam 		if (off == 0) {
729226df7bfSsam 			totlen -= len;
730226df7bfSsam 			continue;
731226df7bfSsam 		}
732226df7bfSsam 		off += len;
733226df7bfSsam 		if (off == totlen) {
734226df7bfSsam 			cp = rxbuf + sizeof (struct ether_header);
735226df7bfSsam 			off = 0;
736226df7bfSsam 			totlen = off0;
737226df7bfSsam 		}
738226df7bfSsam 	}
739226df7bfSsam 	return (top);
740226df7bfSsam bad:
741226df7bfSsam 	m_freem(top);
742226df7bfSsam 	return (0);
743226df7bfSsam }
744226df7bfSsam 
745226df7bfSsam acebakoff(is, txseg, retries)
746226df7bfSsam 	struct ace_softc *is;
747226df7bfSsam 	struct tx_segment *txseg;
748226df7bfSsam 	register int retries;
749226df7bfSsam {
750226df7bfSsam 	register short *pBakNum, random_num;
751226df7bfSsam 	short *pMask;
752226df7bfSsam 
753226df7bfSsam 	pMask = &random_mask_tbl[0];
754226df7bfSsam 	pBakNum = &txseg->tx_backoff[0];
755226df7bfSsam 	while (--retries >= 0) {
756226df7bfSsam 		random_num = (is->is_currnd = (is->is_currnd * 18741)-13849);
757226df7bfSsam 		random_num &= *pMask++;
758226df7bfSsam 		*pBakNum++ = random_num ^ (short)(0xFF00 | 0x00FC);
759226df7bfSsam 	}
760226df7bfSsam }
761226df7bfSsam 
762226df7bfSsam /*
763226df7bfSsam  * Process an ioctl request.
764226df7bfSsam  */
765226df7bfSsam aceioctl(ifp, cmd, data)
766226df7bfSsam 	register struct ifnet *ifp;
767226df7bfSsam 	int cmd;
768226df7bfSsam 	caddr_t data;
769226df7bfSsam {
770c0fcc33dSsam 	register struct ifaddr *ifa = (struct ifaddr *)data;
7717a0e3b47Ssam 	struct acedevice *addr;
772c0fcc33dSsam 	int s = splimp(), error = 0;
773226df7bfSsam 
774226df7bfSsam 	switch (cmd) {
775226df7bfSsam 
776226df7bfSsam 	case SIOCSIFADDR:
777c0fcc33dSsam 		ifp->if_flags |= IFF_UP;
7787a0e3b47Ssam 		switch (ifa->ifa_addr.sa_family) {
7797a0e3b47Ssam #ifdef INET
7807a0e3b47Ssam 		case AF_INET:
7817a0e3b47Ssam 			aceinit(ifp->if_unit);	/* before arpwhohas */
7827a0e3b47Ssam 			((struct arpcom *)ifp)->ac_ipaddr =
7837a0e3b47Ssam 				IA_SIN(ifa)->sin_addr;
784c0fcc33dSsam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
785226df7bfSsam 			break;
7867a0e3b47Ssam #endif
7877a0e3b47Ssam #ifdef NS
7887a0e3b47Ssam 		case AF_NS: {
789e79104c5Ssam 			struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
790e79104c5Ssam 			struct ace_softc *is = &ace_softc[ifp->if_unit];
791226df7bfSsam 
7927a0e3b47Ssam 			if (!ns_nullhost(*ina)) {
7937a0e3b47Ssam 				ifp->if_flags &= ~IFF_RUNNING;
7947a0e3b47Ssam 				addr = (struct acedevice *)
795e79104c5Ssam 				    aceinfo[ifp->if_unit]->ui_addr;
796c0fcc33dSsam 				movow(&addr->csr, CSR_RESET);
797226df7bfSsam 				DELAY(10000);
7987a0e3b47Ssam 				/* set station address & copy addr to arp */
7997a0e3b47Ssam 				acesetetaddr(ifp->if_unit, addr,
8007a0e3b47Ssam 				    ina->x_host.c_host);
8017a0e3b47Ssam 			} else
802e79104c5Ssam 				ina->x_host = *(union ns_host *)is->is_addr;
8037a0e3b47Ssam 			aceinit(ifp->if_unit);
804226df7bfSsam 			break;
805226df7bfSsam 		}
806226df7bfSsam #endif
8077a0e3b47Ssam 		default:
8087a0e3b47Ssam 			aceinit(ifp->if_unit);
8097a0e3b47Ssam 			break;
8107a0e3b47Ssam 		}
8117a0e3b47Ssam 		break;
8127a0e3b47Ssam 
8137a0e3b47Ssam 	case SIOCSIFFLAGS:
8147a0e3b47Ssam 		if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) {
8157a0e3b47Ssam 			addr = (struct acedevice *)
8167a0e3b47Ssam 			    (aceinfo[ifp->if_unit]->ui_addr);
8177a0e3b47Ssam 			movow(&addr->csr, CSR_RESET);
8187a0e3b47Ssam 			ifp->if_flags &= ~IFF_RUNNING;
8197a0e3b47Ssam 		} else if (ifp->if_flags&IFF_UP &&
8207a0e3b47Ssam 		    (ifp->if_flags&IFF_RUNNING) == 0)
8217a0e3b47Ssam 			aceinit(ifp->if_unit);
8227a0e3b47Ssam 		break;
823226df7bfSsam 
824226df7bfSsam 	default:
825226df7bfSsam 		error = EINVAL;
826226df7bfSsam 	}
827226df7bfSsam 	splx(s);
828226df7bfSsam 	return (error);
829226df7bfSsam }
830226df7bfSsam 
831a29de3d6Ssam acesetup(unit)
832226df7bfSsam 	int unit;
833226df7bfSsam {
834226df7bfSsam 	register struct ace_softc *is = &ace_softc[unit];
835226df7bfSsam 	register char *pData1;
836a29de3d6Ssam 	register short i;
837a29de3d6Ssam 	struct acedevice *addr;
838226df7bfSsam 
839a29de3d6Ssam 	bzero(is->is_dpm, 16384*2);
840226df7bfSsam 	is->is_currnd = 49123;
841a29de3d6Ssam 	addr = (struct acedevice *)aceinfo[unit]->ui_addr;
842226df7bfSsam 	is->is_segboundry = (addr->segb >> 11) & 0xf;
843a29de3d6Ssam 	pData1 = is->is_dpm + (is->is_segboundry << 11);
844226df7bfSsam 	for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) {
845226df7bfSsam 		acebakoff(is, (struct tx_segment *)pData1, 15);
846226df7bfSsam 		pData1 += sizeof (struct tx_segment);
847226df7bfSsam 	}
848226df7bfSsam 	is->is_eictr = 0;
849226df7bfSsam 	is->is_eoctr = is->is_txnext = is->is_segboundry;
850226df7bfSsam 	bzero((char *)&is->is_stats, sizeof (is->is_stats));
851226df7bfSsam }
852226df7bfSsam #endif
853