xref: /csrg-svn/sys/tahoe/if/if_ace.c (revision 34864)
134405Skarels /*
234405Skarels  * Copyright (c) 1988 Regents of the University of California.
334405Skarels  * All rights reserved.
434405Skarels  *
534405Skarels  * Redistribution and use in source and binary forms are permitted
6*34864Sbostic  * provided that the above copyright notice and this paragraph are
7*34864Sbostic  * duplicated in all such forms and that any documentation,
8*34864Sbostic  * advertising materials, and other materials related to such
9*34864Sbostic  * distribution and use acknowledge that the software was developed
10*34864Sbostic  * by the University of California, Berkeley.  The name of the
11*34864Sbostic  * University may not be used to endorse or promote products derived
12*34864Sbostic  * from this software without specific prior written permission.
13*34864Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34864Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34864Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1634405Skarels  *
17*34864Sbostic  *	@(#)if_ace.c	7.2 (Berkeley) 06/29/88
1834405Skarels  */
1924007Ssam 
2024007Ssam /*
2124007Ssam  * ACC VERSAbus Ethernet controller
2224007Ssam  */
2324007Ssam #include "ace.h"
2424007Ssam #if NACE > 0
2524007Ssam 
2625694Ssam #include "param.h"
2725694Ssam #include "systm.h"
2825694Ssam #include "mbuf.h"
2925694Ssam #include "buf.h"
3025694Ssam #include "protosw.h"
3125694Ssam #include "socket.h"
3225694Ssam #include "vmmac.h"
3325694Ssam #include "ioctl.h"
3425694Ssam #include "errno.h"
3525694Ssam #include "vmparam.h"
3625855Ssam #include "syslog.h"
3724007Ssam 
3824007Ssam #include "../net/if.h"
3924007Ssam #include "../net/netisr.h"
4024007Ssam #include "../net/route.h"
4125855Ssam #ifdef INET
4224007Ssam #include "../netinet/in.h"
4324007Ssam #include "../netinet/in_systm.h"
4425694Ssam #include "../netinet/in_var.h"
4524007Ssam #include "../netinet/ip.h"
4624007Ssam #include "../netinet/ip_var.h"
4724007Ssam #include "../netinet/if_ether.h"
4825855Ssam #endif
4925855Ssam #ifdef NS
5025855Ssam #include "../netns/ns.h"
5125855Ssam #include "../netns/ns_if.h"
5225855Ssam #endif
5324007Ssam 
5430229Ssam #include "../machine/cpu.h"
5530229Ssam #include "../machine/pte.h"
5630229Ssam 
5724007Ssam #include "../tahoe/mtpr.h"
5824007Ssam #include "../tahoeif/if_acereg.h"
5925694Ssam #include "../tahoevba/vbavar.h"
6024007Ssam 
6124007Ssam int	aceprobe(), aceattach(), acerint(), acecint();
6224007Ssam struct	vba_device *aceinfo[NACE];
6325983Ssam long	acestd[] = { 0 };
6424007Ssam struct	vba_driver acedriver =
6525927Ssam     { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 };
6624007Ssam 
6724007Ssam int	aceinit(), aceoutput(), aceioctl(), acereset();
6824007Ssam struct	mbuf *aceget();
6924007Ssam 
7024007Ssam /*
7124007Ssam  * Ethernet software status per interface.
7224007Ssam  *
7324007Ssam  * Each interface is referenced by a network interface structure,
7424007Ssam  * is_if, which the routing code uses to locate the interface.
7524007Ssam  * This structure contains the output queue for the interface, its address, ...
7624007Ssam  */
7724007Ssam struct	ace_softc {
7824007Ssam 	struct	arpcom is_ac;		/* Ethernet common part	*/
7924007Ssam #define	is_if	is_ac.ac_if		/* network-visible interface */
8024007Ssam #define	is_addr	is_ac.ac_enaddr		/* hardware Ethernet address */
8124007Ssam 	short	is_flags;
8224007Ssam #define	ACEF_OACTIVE	0x1		/* output is active */
8324007Ssam #define	ACEF_RCVPENDING	0x2		/* start rcv in acecint	*/
8424007Ssam 	short	is_promiscuous;		/* true is enabled */
8524007Ssam 	short	is_segboundry;		/* first TX Seg in dpm */
8624007Ssam 	short	is_eictr;		/* Rx segment tracking ctr */
8724007Ssam 	short	is_eoctr;		/* Tx segment tracking ctr */
8824007Ssam 	short	is_txnext;		/* Next available Tx segment */
8924007Ssam 	short	is_currnd;		/* current random backoff */
9024007Ssam 	struct	ace_stats is_stats;	/* holds board statistics */
9124007Ssam 	short	is_xcnt;		/* count xmitted segments to be acked
9224007Ssam 					   by the controller */
9325855Ssam 	long	is_ivec;		/* autoconfig interrupt vector base */
9425927Ssam 	struct	pte *is_map;		/* pte map for dual ported memory */
9525927Ssam 	caddr_t	is_dpm;			/* address of mapped memory */
9624007Ssam } ace_softc[NACE];
9724007Ssam extern	struct ifnet loif;
9824007Ssam 
9925855Ssam aceprobe(reg, vi)
10024007Ssam 	caddr_t reg;
10125855Ssam 	struct vba_device *vi;
10224007Ssam {
10325855Ssam 	register br, cvec;		/* must be r12, r11 */
10425855Ssam 	struct acedevice *ap = (struct acedevice *)reg;
10525855Ssam 	struct ace_softc *is = &ace_softc[vi->ui_unit];
10624007Ssam 
10724007Ssam #ifdef lint
10830295Ssam 	br = 0; cvec = br; br = cvec;
10924007Ssam 	acerint(0); acecint(0);
11024007Ssam #endif
11124007Ssam 	if (badaddr(reg, 2))
11225855Ssam 		return (0);
11325855Ssam 	movow(&ap->csr, CSR_RESET);
11424007Ssam 	DELAY(10000);
11525855Ssam #ifdef notdef
11625855Ssam 	/*
11725855Ssam 	 * Select two spaces for the interrupts aligned to an
11825855Ssam 	 * eight vector boundary and fitting in 8 bits (as
11925855Ssam 	 * required by the controller) -- YECH.  The controller
12025855Ssam 	 * will be notified later at initialization time.
12125855Ssam 	 */
12225855Ssam 	if ((vi->ui_hd->vh_lastiv -= 2) > 0xff)
12325855Ssam 		vi->ui_hd->vh_lastiv  = 0x200;
12425855Ssam 	is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7;
12525855Ssam #else
12625855Ssam 	is->is_ivec = 0x90+vi->ui_unit*8;
12725855Ssam #endif
12825855Ssam 	br = 0x14, cvec = is->is_ivec;		/* XXX */
12925855Ssam 	return (sizeof (*ap));
13024007Ssam }
13124007Ssam 
13224007Ssam /*
13324007Ssam  * Interface exists: make available by filling in network interface
13424007Ssam  * record.  System will initialize the interface when it is ready
13524007Ssam  * to accept packets.
13624007Ssam  */
13724007Ssam aceattach(ui)
13824007Ssam 	struct vba_device *ui;
13924007Ssam {
14024007Ssam 	register short unit = ui->ui_unit;
14124007Ssam 	register struct ace_softc *is = &ace_softc[unit];
14224007Ssam 	register struct ifnet *ifp = &is->is_if;
14324007Ssam 	register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
14424007Ssam 	register short *wp, i;
14524007Ssam 
14624007Ssam 	ifp->if_unit = unit;
14724007Ssam 	ifp->if_name = "ace";
14824007Ssam 	ifp->if_mtu = ETHERMTU;
14924007Ssam 	/*
15029408Ssam 	 * Get station's addresses and set multicast hash table.
15124007Ssam 	 */
15229408Ssam 	for (wp = (short *)addr->station, i = 0; i < 6; i++)
15329408Ssam 		is->is_addr[i] = ~*wp++;
15429408Ssam 	printf("ace%d: hardware address %s\n", unit,
15529408Ssam 	    ether_sprintf(is->is_addr));
15624007Ssam 	is->is_promiscuous = 0;
15729408Ssam 	for (wp = (short *)addr->hash, i =  0; i < 8; i++)
15829408Ssam 		movow(wp++, ~0xf);
15925694Ssam 	movow(&addr->bcastena[0], ~0xffff);
16025694Ssam 	movow(&addr->bcastena[1], ~0xffff);
16125927Ssam 	/*
16225927Ssam 	 * Allocate and map dual ported VERSAbus memory.
16325927Ssam 	 */
16431735Skarels 	if (vbmemalloc(32, (caddr_t)ui->ui_flags,
16531735Skarels 	    &is->is_map, &is->is_dpm) == 0) {
16631735Skarels 		printf("ace%d: can't allocate VERSAbus memory map\n", unit);
16731735Skarels 		return;
16831735Skarels 	}
16925927Ssam 
17024007Ssam 	ifp->if_init = aceinit;
17124007Ssam 	ifp->if_output = aceoutput;
17224007Ssam 	ifp->if_ioctl = aceioctl;
17324007Ssam 	ifp->if_reset = acereset;
17425694Ssam 	ifp->if_flags = IFF_BROADCAST;
17524007Ssam 	if_attach(ifp);
17624007Ssam }
17724007Ssam 
17824007Ssam /*
17924007Ssam  * Reset of interface after "system" reset.
18024007Ssam  */
18124007Ssam acereset(unit, vban)
18224007Ssam 	int unit, vban;
18324007Ssam {
18424007Ssam 	register struct vba_device *ui;
18524007Ssam 
18624007Ssam 	if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 ||
18724007Ssam 	    ui->ui_vbanum != vban)
18824007Ssam 		return;
18924007Ssam 	printf(" ace%d", unit);
19024007Ssam 	aceinit(unit);
19124007Ssam }
19224007Ssam 
19324007Ssam /*
19424007Ssam  * Initialization of interface; clear recorded pending operations
19524007Ssam  */
19624007Ssam aceinit(unit)
19724007Ssam 	int unit;
19824007Ssam {
19924007Ssam 	register struct ace_softc *is = &ace_softc[unit];
20024007Ssam 	register struct vba_device *ui = aceinfo[unit];
20124007Ssam 	register struct acedevice *addr;
20224007Ssam 	register struct ifnet *ifp = &is->is_if;
20324007Ssam 	register short Csr;
20425694Ssam 	register int s;
20524007Ssam 
20625694Ssam 	if (ifp->if_addrlist == (struct ifaddr *)0)
20724007Ssam 		return;
20824007Ssam 	if ((ifp->if_flags & IFF_RUNNING) == 0) {
20924007Ssam 		/*
21024007Ssam 		 * Reset the controller, initialize the recieve buffers,
21124007Ssam 		 * and turn the controller on again and set board online.
21224007Ssam 		 */
21324007Ssam 		addr = (struct acedevice *)ui->ui_addr;
21424007Ssam 		s = splimp();
21525694Ssam 		movow(&addr->csr, CSR_RESET);
21624007Ssam 		DELAY(10000);
21724007Ssam 
21824007Ssam 		/*
21925927Ssam 		 * Clean up dpm since the controller might
22025927Ssam 		 * jumble dpm after reset.
22124007Ssam 		 */
22225927Ssam 		acesetup(unit);
22325694Ssam 		movow(&addr->csr, CSR_GO);
22424007Ssam 		Csr = addr->csr;
22524007Ssam 		if (Csr & CSR_ACTIVE) {
22625855Ssam 			movow(&addr->ivct, is->is_ivec);
22724007Ssam 			Csr |= CSR_IENA | is->is_promiscuous;
22825694Ssam 			movow(&addr->csr, Csr);
22924007Ssam 			is->is_flags = 0;
23024007Ssam 			is->is_xcnt = 0;
23125694Ssam 			is->is_if.if_flags |= IFF_RUNNING;
23224007Ssam 		}
23324007Ssam 		splx(s);
23424007Ssam 	}
23525694Ssam 	if (is->is_if.if_snd.ifq_head)
23625927Ssam 		acestart(unit);
23724007Ssam }
23824007Ssam 
23924007Ssam /*
24024007Ssam  * Start output on interface.
24124007Ssam  * Get another datagram to send off of the interface queue,
24224007Ssam  * and map it to the interface before starting the output.
24324007Ssam  */
24425927Ssam acestart(unit)
24525927Ssam 	int unit;
24624007Ssam {
24724007Ssam 	register struct tx_segment *txs;
24825694Ssam 	register long len;
24925694Ssam 	register int s;
25024007Ssam 	register struct ace_softc *is = &ace_softc[unit];
25124007Ssam 	struct mbuf *m;
25225694Ssam 	short retries;
25324007Ssam 
25425927Ssam 	if (is->is_flags & ACEF_OACTIVE)
25525927Ssam 		return;
25625927Ssam 	is->is_flags |= ACEF_OACTIVE;
25724007Ssam again:
25824007Ssam 	txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11));
25924007Ssam 	if (txs->tx_csr & TCS_TBFULL) {
26024007Ssam 		is->is_stats.tx_busy++;
26125927Ssam 		is->is_flags &= ~ACEF_OACTIVE;
26224007Ssam 		return;
26324007Ssam 	}
26425694Ssam 	s = splimp();
26524007Ssam 	IF_DEQUEUE(&is->is_if.if_snd, m);
26625694Ssam 	splx(s);
26725927Ssam 	if (m == 0) {
26825927Ssam 		is->is_flags &= ~ACEF_OACTIVE;
26924007Ssam 		return;
27025927Ssam 	}
27124007Ssam 	len = aceput(unit, txs->tx_data, m);
27224007Ssam 	retries = txs->tx_csr & TCS_RTC;
27324007Ssam 	if (retries > 0)
27424007Ssam 		acebakoff(is, txs, retries);
27524007Ssam 
27624007Ssam 	/*
27724007Ssam 	 * Ensure minimum packet length.
27824007Ssam 	 * This makes the safe assumtion that there are no virtual holes
27924007Ssam 	 * after the data.
28024007Ssam 	 * For security, it might be wise to zero out the added bytes,
28124007Ssam 	 * but we're mainly interested in speed at the moment.
28224007Ssam 	 */
28324007Ssam 	if (len - sizeof (struct ether_header) < ETHERMIN)
28424007Ssam 		len = ETHERMIN + sizeof (struct ether_header);
28524007Ssam 	if (++is->is_txnext > SEG_MAX)
28624007Ssam 		is->is_txnext = is->is_segboundry;
28724007Ssam 	is->is_if.if_opackets++;
28824007Ssam 	is->is_xcnt++;
28924007Ssam 	len = (len & 0x7fff) | TCS_TBFULL;
29025694Ssam 	movow(txs, len);
29124007Ssam 	goto again;
29224007Ssam }
29324007Ssam 
29424007Ssam /*
29524007Ssam  * Transmit done interrupt.
29624007Ssam  */
29724007Ssam acecint(unit)
29824007Ssam 	int unit;
29924007Ssam {
30024007Ssam 	register struct ace_softc *is = &ace_softc[unit];
30124007Ssam 	register struct tx_segment *txseg;
30225694Ssam 	short eostat;
30324007Ssam 
30424007Ssam 	if (is->is_xcnt <= 0)  {
30525855Ssam 		log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n",
30624007Ssam 		    unit, is->is_xcnt);
30724007Ssam 		is->is_xcnt = 0;
30825694Ssam 		if (is->is_if.if_snd.ifq_head)
30925927Ssam 			acestart(unit);
31024007Ssam 		return;
31124007Ssam 	}
31224007Ssam 	is->is_xcnt--;
31324007Ssam 	txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm);
31424007Ssam 	eostat = txseg->tx_csr;
31524007Ssam 	if ((eostat & TCS_TBFULL) == 0) {
31624007Ssam 		is->is_stats.tx_retries += eostat & TCS_RTC;
31724007Ssam 		if (eostat & TCS_RTFAIL)  {
31824007Ssam 			is->is_stats.tx_discarded++;
31924007Ssam 			is->is_if.if_oerrors++;
32024007Ssam 		} else
32124007Ssam 			is->is_stats.tx_datagrams++;
32224007Ssam 		if (++is->is_eoctr >= 16)
32324007Ssam 			is->is_eoctr = is->is_segboundry;
32424007Ssam 	}
32525694Ssam 	if (is->is_if.if_snd.ifq_head)
32625927Ssam 		acestart(unit);
32724007Ssam }
32824007Ssam 
32924007Ssam /*
33024007Ssam  * Ethernet interface receiver interrupt.
33124007Ssam  * If input error just drop packet.
33224007Ssam  * Otherwise purge input buffered data path and examine
33324007Ssam  * packet to determine type.  If can't determine length
33424007Ssam  * from type, then have to drop packet.  Othewise decapsulate
33524007Ssam  * packet based on type and pass to type specific higher-level
33624007Ssam  * input routine.
33724007Ssam  */
33824007Ssam acerint(unit)
33924007Ssam 	int unit;
34024007Ssam {
34124007Ssam 	register struct ace_softc *is = &ace_softc[unit];
34224007Ssam 	register struct ifqueue *inq;
34324007Ssam 	register struct ether_header *ace;
34424007Ssam 	register struct rx_segment *rxseg;
34524007Ssam 	int len, s, off, resid;
34624007Ssam 	struct mbuf *m;
34724007Ssam 	short eistat;
34824007Ssam 
34925694Ssam 	if ((is->is_if.if_flags&IFF_RUNNING) == 0)
35025694Ssam 		return;
35124007Ssam again:
35224007Ssam 	rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm);
35324007Ssam 	eistat = rxseg->rx_csr;
35424007Ssam 	if ((eistat & RCS_RBFULL) == 0)
35524007Ssam 		return;
35624007Ssam 	is->is_if.if_ipackets++;
35724007Ssam 	if (++is->is_eictr >= is->is_segboundry)
35824007Ssam 		is->is_eictr = 0;
35924007Ssam 	len = eistat & RCS_RBC;
36024007Ssam 	if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) ||
36124007Ssam 	    len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) {
36224007Ssam 		if (eistat & RCS_ROVRN)
36324007Ssam 			is->is_stats.rx_overruns++;
36424007Ssam 		if (eistat & RCS_RCRC)
36524007Ssam 			is->is_stats.rx_crc_errors++;
36624007Ssam 		if (eistat & RCS_RODD)
36724007Ssam 			is->is_stats.rx_align_errors++;
36824007Ssam 		if (len < ET_MINLEN)
36924007Ssam 			is->is_stats.rx_underruns++;
37024007Ssam 		if (len > ET_MAXLEN+CRC_SIZE)
37124007Ssam 			is->is_stats.rx_overruns++;
37224007Ssam 		is->is_if.if_ierrors++;
37324007Ssam 		rxseg->rx_csr = 0;
37424007Ssam 		return;
37524007Ssam 	} else
37624007Ssam 		is->is_stats.rx_datagrams++;
37724007Ssam 	ace = (struct ether_header *)rxseg->rx_data;
37824007Ssam 	len -= sizeof (struct ether_header);
37924007Ssam 	/*
38025694Ssam 	 * Deal with trailer protocol: if type is trailer
38124007Ssam 	 * get true type from first 16-bit word past data.
38224007Ssam 	 * Remember that type was trailer by setting off.
38324007Ssam 	 */
38424007Ssam 	ace->ether_type = ntohs((u_short)ace->ether_type);
38524007Ssam #define	acedataaddr(ace, off, type) \
38624007Ssam     ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off))))
38725694Ssam 	if (ace->ether_type >= ETHERTYPE_TRAIL &&
38825694Ssam 	    ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
38925694Ssam 		off = (ace->ether_type - ETHERTYPE_TRAIL) * 512;
39024007Ssam 		if (off >= ETHERMTU)
39124007Ssam 			goto setup;		/* sanity */
39224007Ssam 		ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *));
39324007Ssam 		resid = ntohs(*(acedataaddr(ace, off+2, u_short *)));
39424007Ssam 		if (off + resid > len)
39524007Ssam 			goto setup;		/* sanity */
39624007Ssam 		len = off + resid;
39724007Ssam 	} else
39824007Ssam 		off = 0;
39924007Ssam 	if (len == 0)
40024007Ssam 		goto setup;
40124007Ssam 
40224007Ssam 	/*
40324007Ssam 	 * Pull packet off interface.  Off is nonzero if packet
40424007Ssam 	 * has trailing header; aceget will then force this header
40524007Ssam 	 * information to be at the front, but we still have to drop
40624007Ssam 	 * the type and length which are at the front of any trailer data.
40724007Ssam 	 */
40825855Ssam 	m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if);
40924007Ssam 	if (m == 0)
41024007Ssam 		goto setup;
41124007Ssam 	if (off) {
41225855Ssam 		struct ifnet *ifp;
41325855Ssam 
41425855Ssam 		ifp = *(mtod(m, struct ifnet **));
41524007Ssam 		m->m_off += 2 * sizeof (u_short);
41624007Ssam 		m->m_len -= 2 * sizeof (u_short);
41725855Ssam 		*(mtod(m, struct ifnet **)) = ifp;
41824007Ssam 	}
41924007Ssam 	switch (ace->ether_type) {
42024007Ssam 
42124007Ssam #ifdef INET
42225694Ssam 	case ETHERTYPE_IP:
42324007Ssam 		schednetisr(NETISR_IP);
42424007Ssam 		inq = &ipintrq;
42524007Ssam 		break;
42625855Ssam #endif
42724007Ssam 
42825694Ssam 	case ETHERTYPE_ARP:
42924007Ssam 		arpinput(&is->is_ac, m);
43024007Ssam 		goto setup;
43125694Ssam #ifdef NS
43225694Ssam 	case ETHERTYPE_NS:
43325694Ssam 		schednetisr(NETISR_NS);
43425694Ssam 		inq = &nsintrq;
43525694Ssam 		break;
43625694Ssam 
43725694Ssam #endif
43824007Ssam 	default:
43924007Ssam 		m_freem(m);
44024007Ssam 		goto setup;
44124007Ssam 	}
44224007Ssam 	if (IF_QFULL(inq)) {
44324007Ssam 		IF_DROP(inq);
44424007Ssam 		m_freem(m);
44524007Ssam 		goto setup;
44624007Ssam 	}
44724007Ssam 	s = splimp();
44824007Ssam 	IF_ENQUEUE(inq, m);
44924007Ssam 	splx(s);
45024007Ssam setup:
45124007Ssam 	rxseg->rx_csr = 0;
45224007Ssam 	goto again;
45324007Ssam }
45424007Ssam 
45524007Ssam /*
45624007Ssam  * Ethernet output routine.
45724007Ssam  * Encapsulate a packet of type family for the local net.
45824007Ssam  * Use trailer local net encapsulation if enough data in first
45924007Ssam  * packet leaves a multiple of 512 bytes of data in remainder.
46024007Ssam  */
46124007Ssam aceoutput(ifp, m0, dst)
46224007Ssam 	struct ifnet *ifp;
46324007Ssam 	struct mbuf *m0;
46424007Ssam 	struct sockaddr *dst;
46524007Ssam {
46624007Ssam 	register struct ace_softc *is = &ace_softc[ifp->if_unit];
46724007Ssam 	register struct mbuf *m = m0;
46824007Ssam 	register struct ether_header *ace;
46924007Ssam 	register int off;
47024007Ssam 	struct mbuf *mcopy = (struct mbuf *)0;
47125957Ssam 	int type, s, error, usetrailers;
47225694Ssam 	u_char edst[6];
47324007Ssam 	struct in_addr idst;
47424007Ssam 
47525855Ssam 	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
47625855Ssam 		error = ENETDOWN;
47725855Ssam 		goto bad;
47825855Ssam 	}
47924007Ssam 	switch (dst->sa_family) {
48024007Ssam 
48124007Ssam #ifdef INET
48224007Ssam 	case AF_INET:
48324007Ssam 		idst = ((struct sockaddr_in *)dst)->sin_addr;
48425957Ssam 		if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers))
48524007Ssam 			return (0);	/* if not yet resolved */
48625694Ssam 		if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr,
48725937Ssam 		    sizeof (edst)))
48824007Ssam 			mcopy = m_copy(m, 0, (int)M_COPYALL);
48924007Ssam 		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
49025957Ssam 		if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
49124007Ssam 		    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
49225694Ssam 			type = ETHERTYPE_TRAIL + (off>>9);
49324007Ssam 			m->m_off -= 2 * sizeof (u_short);
49424007Ssam 			m->m_len += 2 * sizeof (u_short);
49525694Ssam 			*mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
49624007Ssam 			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
49724007Ssam 			goto gottrailertype;
49824007Ssam 		}
49925694Ssam 		type = ETHERTYPE_IP;
50024007Ssam 		off = 0;
50124007Ssam 		goto gottype;
50224007Ssam #endif
50325694Ssam #ifdef NS
50425694Ssam 	case AF_NS:
50525694Ssam  		bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
50625694Ssam 		    (caddr_t)edst, sizeof (edst));
50725694Ssam 		if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst)))
50825694Ssam 			mcopy = m_copy(m, 0, (int)M_COPYALL);
50925694Ssam 		else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost,
51025694Ssam 		    sizeof(edst)))
51125694Ssam 			return(looutput(&loif, m, dst));
51225694Ssam 		type = ETHERTYPE_NS;
51325694Ssam 		off = 0;
51425694Ssam 		goto gottype;
51525694Ssam #endif
51624007Ssam 	case AF_UNSPEC:
51724007Ssam 		ace = (struct ether_header *)dst->sa_data;
51825694Ssam 		bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst));
51924007Ssam 		type = ace->ether_type;
52024007Ssam 		goto gottype;
52124007Ssam 
52224007Ssam 	default:
52325855Ssam 		log(LOG_ERR, "ace%d: can't handle af%d\n",
52424007Ssam 		    ifp->if_unit, dst->sa_family);
52524007Ssam 		error = EAFNOSUPPORT;
52624007Ssam 		goto bad;
52724007Ssam 	}
52824007Ssam 
52924007Ssam gottrailertype:
53024007Ssam 	/*
53124007Ssam 	 * Packet to be sent as trailer: move first packet
53224007Ssam 	 * (control information) to end of chain.
53324007Ssam 	 */
53424007Ssam 	while (m->m_next)
53524007Ssam 		m = m->m_next;
53624007Ssam 	m->m_next = m0;
53724007Ssam 	m = m0->m_next;
53824007Ssam 	m0->m_next = 0;
53924007Ssam 	m0 = m;
54024007Ssam 
54124007Ssam gottype:
54224007Ssam 	/*
54324007Ssam 	 * Add local net header.  If no space in first mbuf,
54424007Ssam 	 * allocate another.
54524007Ssam 	 */
54624007Ssam 	if (m->m_off > MMAXOFF ||
54724007Ssam 	    MMINOFF + sizeof (struct ether_header) > m->m_off) {
54824007Ssam 		m = m_get(M_DONTWAIT, MT_HEADER);
54924007Ssam 		if (m == 0) {
55024007Ssam 			error = ENOBUFS;
55124007Ssam 			goto bad;
55224007Ssam 		}
55324007Ssam 		m->m_next = m0;
55424007Ssam 		m->m_off = MMINOFF;
55524007Ssam 		m->m_len = sizeof (struct ether_header);
55624007Ssam 	} else {
55724007Ssam 		m->m_off -= sizeof (struct ether_header);
55824007Ssam 		m->m_len += sizeof (struct ether_header);
55924007Ssam 	}
56024007Ssam 	ace = mtod(m, struct ether_header *);
56125694Ssam 	bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst));
56225694Ssam 	bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost,
56325694Ssam 	    sizeof (is->is_addr));
56424007Ssam 	ace->ether_type = htons((u_short)type);
56524007Ssam 
56624007Ssam 	/*
56724007Ssam 	 * Queue message on interface, and start output if interface
56824007Ssam 	 * not yet active.
56924007Ssam 	 */
57024007Ssam 	s = splimp();
57124007Ssam 	if (IF_QFULL(&ifp->if_snd)) {
57224007Ssam 		IF_DROP(&ifp->if_snd);
57324007Ssam 		error = ENOBUFS;
57424007Ssam 		goto qfull;
57524007Ssam 	}
57624007Ssam 	IF_ENQUEUE(&ifp->if_snd, m);
57724007Ssam 	splx(s);
57825927Ssam 	acestart(ifp->if_unit);
57924007Ssam 	return (mcopy ? looutput(&loif, mcopy, dst) : 0);
58024007Ssam qfull:
58124007Ssam 	m0 = m;
58224007Ssam 	splx(s);
58324007Ssam bad:
58424007Ssam 	m_freem(m0);
58524007Ssam 	if (mcopy)
58624007Ssam 		m_freem(mcopy);
58724007Ssam 	return (error);
58824007Ssam }
58924007Ssam 
59024007Ssam /*
59124007Ssam  * Routine to copy from mbuf chain to transmit buffer on the VERSAbus
59224007Ssam  * If packet size is less than the minimum legal size,
59324007Ssam  * the buffer is expanded.  We probably should zero out the extra
59424007Ssam  * bytes for security, but that would slow things down.
59524007Ssam  */
59625694Ssam /*ARGSUSED*/
59724007Ssam aceput(unit, txbuf, m)
59825855Ssam 	int unit;
59925694Ssam 	char *txbuf;
60024007Ssam 	struct mbuf *m;
60124007Ssam {
60225855Ssam 	register u_char *bp, *mcp;
60325855Ssam 	register short *s1, *s2;
60425694Ssam 	register u_int len;
60524007Ssam 	register struct mbuf *mp;
60625694Ssam 	int total;
60724007Ssam 
60824007Ssam 	total = 0;
60925694Ssam 	bp = (u_char *)txbuf;
61025694Ssam 	for (mp = m; (mp); mp = mp->m_next) {
61124007Ssam 		len = mp->m_len;
61224007Ssam 		if (len == 0)
61324007Ssam 			continue;
61424007Ssam 		total += len;
61524007Ssam 		mcp = mtod(mp, u_char *);
61624007Ssam 		if (((int)mcp & 01) && ((int)bp & 01)) {
61724007Ssam 			/* source & destination at odd addresses */
61825694Ssam 			movob(bp++, *mcp++);
61924007Ssam 			--len;
62024007Ssam 		}
62124007Ssam 		if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) {
62225694Ssam 			register u_int l;
62324007Ssam 
62424007Ssam 			s1 = (short *)bp;
62524007Ssam 			s2 = (short *)mcp;
62624007Ssam 			l = len >> 1;		/* count # of shorts */
62725694Ssam 			while (l-- != 0)
62825694Ssam 				movow(s1++, *s2++);
62924007Ssam 			len &= 1;		/* # remaining bytes */
63024007Ssam 			bp = (u_char *)s1;
63124007Ssam 			mcp = (u_char *)s2;
63224007Ssam 		}
63325694Ssam 		while (len-- != 0)
63425694Ssam 			movob(bp++, *mcp++);
63524007Ssam 	}
63624007Ssam 	m_freem(m);
63724007Ssam 	return (total);
63824007Ssam }
63924007Ssam 
64024007Ssam /*
64124007Ssam  * Routine to copy from VERSAbus memory into mbufs.
64224007Ssam  *
64324007Ssam  * Warning: This makes the fairly safe assumption that
64424007Ssam  * mbufs have even lengths.
64524007Ssam  */
64625694Ssam /*ARGSUSED*/
64724007Ssam struct mbuf *
64825855Ssam aceget(rxbuf, totlen, off0, ifp)
64924007Ssam 	u_char *rxbuf;
65024007Ssam 	int totlen, off0;
65125855Ssam 	struct ifnet *ifp;
65224007Ssam {
65325855Ssam 	register u_char *cp, *mcp;
65424007Ssam 	register int tlen;
65524007Ssam 	register struct mbuf *m;
65624007Ssam 	struct mbuf *top = 0, **mp = &top;
65724007Ssam 	int len, off = off0;
65824007Ssam 
65924007Ssam 	cp = rxbuf + sizeof (struct ether_header);
66024007Ssam 	while (totlen > 0) {
66124007Ssam 		MGET(m, M_DONTWAIT, MT_DATA);
66224007Ssam 		if (m == 0)
66324007Ssam 			goto bad;
66424007Ssam 		if (off) {
66524007Ssam 			len = totlen - off;
66624007Ssam 			cp = rxbuf + sizeof (struct ether_header) + off;
66724007Ssam 		} else
66824007Ssam 			len = totlen;
66925855Ssam 		if (ifp)
67025855Ssam 			len += sizeof(ifp);
67129563Ssam 		if (len >= NBPG) {
67229563Ssam 			MCLGET(m);
67329563Ssam 			if (m->m_len == CLBYTES)
67429563Ssam 				m->m_len = len = MIN(len, CLBYTES);
67529563Ssam 			else
67624007Ssam 				m->m_len = len = MIN(MLEN, len);
67724007Ssam 		} else {
67824007Ssam 			m->m_len = len = MIN(MLEN, len);
67924007Ssam 			m->m_off = MMINOFF;
68024007Ssam 		}
68124007Ssam 		mcp = mtod(m, u_char *);
68225855Ssam 		if (ifp) {
68325855Ssam 			/*
68425855Ssam 			 * Prepend interface pointer to first mbuf.
68525855Ssam 			 */
68625855Ssam 			*(mtod(m, struct ifnet **)) = ifp;
68725855Ssam 			mcp += sizeof(ifp);
68825855Ssam 			len -= sizeof(ifp);
68925855Ssam 			ifp = (struct ifnet *)0;
69025855Ssam 		}
69124007Ssam 		/*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/
69224007Ssam 		/*cp += len; mcp += len;*/
69324007Ssam 		tlen = len;
69424007Ssam 		if (((int)mcp & 01) && ((int)cp & 01)) {
69524007Ssam 			/* source & destination at odd addresses */
69624007Ssam 			*mcp++ = *cp++;
69724007Ssam 			--tlen;
69824007Ssam 		}
69924007Ssam 		if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) {
70024007Ssam 			register short *s1, *s2;
70124007Ssam 			register int l;
70224007Ssam 
70324007Ssam 			s1 = (short *)mcp;
70424007Ssam 			s2 = (short *)cp;
70524007Ssam 			l = tlen >> 1;		/* count # of shorts */
70624007Ssam 			while (l-- > 0)		/* copy shorts */
70724007Ssam 				*s1++ = *s2++;
70824007Ssam 			tlen &= 1;		/* # remaining bytes */
70924007Ssam 			mcp = (u_char *)s1;
71024007Ssam 			cp = (u_char *)s2;
71124007Ssam 		}
71224007Ssam 		while (tlen-- > 0)
71324007Ssam 			*mcp++ = *cp++;
71424007Ssam 		*mp = m;
71524007Ssam 		mp = &m->m_next;
71624007Ssam 		if (off == 0) {
71724007Ssam 			totlen -= len;
71824007Ssam 			continue;
71924007Ssam 		}
72024007Ssam 		off += len;
72124007Ssam 		if (off == totlen) {
72224007Ssam 			cp = rxbuf + sizeof (struct ether_header);
72324007Ssam 			off = 0;
72424007Ssam 			totlen = off0;
72524007Ssam 		}
72624007Ssam 	}
72724007Ssam 	return (top);
72824007Ssam bad:
72924007Ssam 	m_freem(top);
73024007Ssam 	return (0);
73124007Ssam }
73224007Ssam 
73329408Ssam /* backoff table masks */
73429408Ssam short	random_mask_tbl[16] = {
73529563Ssam 	0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0,
73629563Ssam 	0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0
73729408Ssam };
73829408Ssam 
73924007Ssam acebakoff(is, txseg, retries)
74024007Ssam 	struct ace_softc *is;
74124007Ssam 	struct tx_segment *txseg;
74224007Ssam 	register int retries;
74324007Ssam {
74424007Ssam 	register short *pBakNum, random_num;
74524007Ssam 	short *pMask;
74624007Ssam 
74724007Ssam 	pMask = &random_mask_tbl[0];
74824007Ssam 	pBakNum = &txseg->tx_backoff[0];
74924007Ssam 	while (--retries >= 0) {
75024007Ssam 		random_num = (is->is_currnd = (is->is_currnd * 18741)-13849);
75124007Ssam 		random_num &= *pMask++;
75229563Ssam 		*pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc);
75324007Ssam 	}
75424007Ssam }
75524007Ssam 
75624007Ssam /*
75724007Ssam  * Process an ioctl request.
75824007Ssam  */
75924007Ssam aceioctl(ifp, cmd, data)
76024007Ssam 	register struct ifnet *ifp;
76124007Ssam 	int cmd;
76224007Ssam 	caddr_t data;
76324007Ssam {
76425694Ssam 	register struct ifaddr *ifa = (struct ifaddr *)data;
76525855Ssam 	struct acedevice *addr;
76625694Ssam 	int s = splimp(), error = 0;
76724007Ssam 
76824007Ssam 	switch (cmd) {
76924007Ssam 
77024007Ssam 	case SIOCSIFADDR:
77125694Ssam 		ifp->if_flags |= IFF_UP;
77225855Ssam 		switch (ifa->ifa_addr.sa_family) {
77325855Ssam #ifdef INET
77425855Ssam 		case AF_INET:
77525855Ssam 			aceinit(ifp->if_unit);	/* before arpwhohas */
77625855Ssam 			((struct arpcom *)ifp)->ac_ipaddr =
77725855Ssam 				IA_SIN(ifa)->sin_addr;
77825855Ssam 			arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
77925855Ssam 			break;
78025855Ssam #endif
78125855Ssam #ifdef NS
78225855Ssam 		case AF_NS: {
78325937Ssam 			struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
78425937Ssam 			struct ace_softc *is = &ace_softc[ifp->if_unit];
78525855Ssam 
78625855Ssam 			if (!ns_nullhost(*ina)) {
78725855Ssam 				ifp->if_flags &= ~IFF_RUNNING;
78825855Ssam 				addr = (struct acedevice *)
78925937Ssam 				    aceinfo[ifp->if_unit]->ui_addr;
79025855Ssam 				movow(&addr->csr, CSR_RESET);
79125855Ssam 				DELAY(10000);
79225855Ssam 				/* set station address & copy addr to arp */
79329408Ssam 				acesetaddr(ifp->if_unit, addr,
79425855Ssam 				    ina->x_host.c_host);
79525855Ssam 			} else
79625937Ssam 				ina->x_host = *(union ns_host *)is->is_addr;
79725855Ssam 			aceinit(ifp->if_unit);
79825855Ssam 			break;
79925855Ssam 		}
80025855Ssam #endif
80125855Ssam 		default:
80225855Ssam 			aceinit(ifp->if_unit);
80325855Ssam 			break;
80425855Ssam 		}
80524007Ssam 		break;
80624007Ssam 
80725855Ssam 	case SIOCSIFFLAGS:
80825855Ssam 		if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) {
80925855Ssam 			addr = (struct acedevice *)
81025855Ssam 			    (aceinfo[ifp->if_unit]->ui_addr);
81125855Ssam 			movow(&addr->csr, CSR_RESET);
81225855Ssam 			ifp->if_flags &= ~IFF_RUNNING;
81325855Ssam 		} else if (ifp->if_flags&IFF_UP &&
81425855Ssam 		    (ifp->if_flags&IFF_RUNNING) == 0)
81525855Ssam 			aceinit(ifp->if_unit);
81624007Ssam 		break;
81724007Ssam 
81824007Ssam 	default:
81924007Ssam 		error = EINVAL;
82024007Ssam 	}
82124007Ssam 	splx(s);
82224007Ssam 	return (error);
82324007Ssam }
82424007Ssam 
82529408Ssam /*
82629408Ssam  * Set the on-board station address, then read it back
82729408Ssam  * to initialize the address used by ARP (among others).
82829408Ssam  */
82929408Ssam acesetaddr(unit, addr, station)
83029408Ssam 	short unit;
83129408Ssam 	struct acedevice *addr;
83230295Ssam 	u_char *station;
83329408Ssam {
83429408Ssam 	struct ace_softc *is = &ace_softc[unit];
83529408Ssam 	register short *wp, i;
83629408Ssam 
83729408Ssam 	for (wp = (short *)addr->station, i = 0; i < 6; i++)
83829408Ssam 		movow(wp++, ~*station++);
83929408Ssam 	for (wp = (short *)addr->station, i = 0; i < 6; i++)
84029408Ssam 		is->is_addr[i] = ~*wp++;
84129408Ssam 	printf("ace%d: hardware address %s\n", unit,
84229408Ssam 	    ether_sprintf(is->is_addr));
84329408Ssam }
84429408Ssam 
84529408Ssam /*
84629408Ssam  * Setup the device for use.  Initialize dual-ported memory,
84729408Ssam  * backoff parameters, and various other software state.
84829408Ssam  */
84925927Ssam acesetup(unit)
85024007Ssam 	int unit;
85124007Ssam {
85224007Ssam 	register struct ace_softc *is = &ace_softc[unit];
85325927Ssam 	register char *pData1;
85425694Ssam 	register short i;
85525927Ssam 	struct acedevice *addr;
85624007Ssam 
85725927Ssam 	bzero(is->is_dpm, 16384*2);
85824007Ssam 	is->is_currnd = 49123;
85925927Ssam 	addr = (struct acedevice *)aceinfo[unit]->ui_addr;
86024007Ssam 	is->is_segboundry = (addr->segb >> 11) & 0xf;
86125927Ssam 	pData1 = is->is_dpm + (is->is_segboundry << 11);
86224007Ssam 	for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) {
86324007Ssam 		acebakoff(is, (struct tx_segment *)pData1, 15);
86424007Ssam 		pData1 += sizeof (struct tx_segment);
86524007Ssam 	}
86624007Ssam 	is->is_eictr = 0;
86724007Ssam 	is->is_eoctr = is->is_txnext = is->is_segboundry;
86824007Ssam 	bzero((char *)&is->is_stats, sizeof (is->is_stats));
86924007Ssam }
87024007Ssam #endif
871