if_sbni.c revision 337909
157429Smarkm/*-
257429Smarkm * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
357429Smarkm * Author: Denis I.Timofeev <timofeev@granch.ru>
457429Smarkm *
565668Skris * Redistributon and use in source and binary forms, with or without
660573Skris * modification, are permitted provided that the following conditions
765668Skris * are met:
865668Skris * 1. Redistributions of source code must retain the above copyright
965668Skris *    notice unmodified, this list of conditions, and the following
1065668Skris *    disclaimer.
1165668Skris * 2. Redistributions in binary form must reproduce the above copyright
1260573Skris *    notice, this list of conditions and the following disclaimer in the
1360573Skris *    documentation and/or other materials provided with the distribution.
1465668Skris *
1560573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1665668Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1765668Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1865668Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1965668Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2065668Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2165668Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2265668Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2365668Skris * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
2465668Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2565668Skris * SUCH DAMAGE.
2665668Skris *
2765668Skris */
2865668Skris
2965668Skris#include <sys/cdefs.h>
3065668Skris__FBSDID("$FreeBSD: stable/11/sys/dev/sbni/if_sbni.c 337909 2018-08-16 15:27:19Z brooks $");
3165668Skris
3265668Skris/*
3365668Skris * Device driver for Granch SBNI12 leased line adapters
3465668Skris *
3565668Skris * Revision 2.0.0  1997/08/06
3665668Skris * Initial revision by Alexey Zverev
3760573Skris *
3892555Sdes * Revision 2.0.1 1997/08/11
3965668Skris * Additional internal statistics support (tx statistics)
4065668Skris *
4165668Skris * Revision 2.0.2 1997/11/05
4265668Skris * if_bpf bug has been fixed
4365668Skris *
4465668Skris * Revision 2.0.3 1998/12/20
4565668Skris * Memory leakage has been eliminated in
4665668Skris * the sbni_st and sbni_timeout routines.
4765668Skris *
4865668Skris * Revision 3.0 2000/08/10 by Yaroslav Polyakov
4965668Skris * Support for PCI cards. 4.1 modification.
5065668Skris *
5165668Skris * Revision 3.1 2000/09/12
5265668Skris * Removed extra #defines around bpf functions
5365668Skris *
5465668Skris * Revision 4.0 2000/11/23 by Denis Timofeev
5565668Skris * Completely redesigned the buffer management
5665668Skris *
5765668Skris * Revision 4.1 2001/01/21
5865668Skris * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
5957429Smarkm *
6057429Smarkm * Written with reference to NE2000 driver developed by David Greenman.
6157429Smarkm */
62113908Sdes
6357429Smarkm
6476259Sgreen#include <sys/param.h>
6576259Sgreen#include <sys/bus.h>
6676259Sgreen#include <sys/systm.h>
6757429Smarkm#include <sys/socket.h>
6857429Smarkm#include <sys/sockio.h>
6957429Smarkm#include <sys/mbuf.h>
7060573Skris#include <sys/kernel.h>
7160573Skris#include <sys/priv.h>
7260573Skris#include <sys/proc.h>
7365668Skris#include <sys/callout.h>
7465668Skris#include <sys/syslog.h>
7576259Sgreen#include <sys/random.h>
7676259Sgreen
7776259Sgreen#include <machine/bus.h>
7876259Sgreen#include <sys/rman.h>
7976259Sgreen#include <machine/resource.h>
8076259Sgreen
8176259Sgreen#include <net/if.h>
8276259Sgreen#include <net/if_var.h>
8376259Sgreen#include <net/if_dl.h>
8498675Sdes#include <net/ethernet.h>
8560573Skris#include <net/bpf.h>
8669587Sgreen#include <net/if_types.h>
8768700Sgreen
8868700Sgreen#include <dev/sbni/if_sbnireg.h>
8957429Smarkm#include <dev/sbni/if_sbnivar.h>
9057429Smarkm
9157429Smarkmstatic void	sbni_init(void *);
9257429Smarkmstatic void	sbni_init_locked(struct sbni_softc *);
9357429Smarkmstatic void	sbni_start(struct ifnet *);
9457429Smarkmstatic void	sbni_start_locked(struct ifnet *);
9557429Smarkmstatic int	sbni_ioctl(struct ifnet *, u_long, caddr_t);
9657429Smarkmstatic void	sbni_stop(struct sbni_softc *);
9757429Smarkmstatic void	handle_channel(struct sbni_softc *);
9857429Smarkm
9957429Smarkmstatic void	card_start(struct sbni_softc *);
10057429Smarkmstatic int	recv_frame(struct sbni_softc *);
10157429Smarkmstatic void	send_frame(struct sbni_softc *);
10257429Smarkmstatic int	upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
10357429Smarkmstatic int	skip_tail(struct sbni_softc *, u_int, u_int32_t);
10457429Smarkmstatic void	interpret_ack(struct sbni_softc *, u_int);
10592555Sdesstatic void	download_data(struct sbni_softc *, u_int32_t *);
10692555Sdesstatic void	prepare_to_send(struct sbni_softc *);
10757429Smarkmstatic void	drop_xmit_queue(struct sbni_softc *);
10857429Smarkmstatic int	get_rx_buf(struct sbni_softc *);
10957429Smarkmstatic void	indicate_pkt(struct sbni_softc *);
11057429Smarkmstatic void	change_level(struct sbni_softc *);
11157429Smarkmstatic int	check_fhdr(struct sbni_softc *, u_int *, u_int *,
11265668Skris			   u_int *, u_int *, u_int32_t *);
11365668Skrisstatic int	append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
11457429Smarkmstatic void	timeout_change_level(struct sbni_softc *);
11557429Smarkmstatic void	send_frame_header(struct sbni_softc *, u_int32_t *);
11657429Smarkmstatic void	set_initial_values(struct sbni_softc *, struct sbni_flags);
11757429Smarkm
11857429Smarkmstatic u_int32_t	calc_crc32(u_int32_t, caddr_t, u_int);
11957429Smarkmstatic timeout_t	sbni_timeout;
12057429Smarkm
12176259Sgreenstatic __inline u_char	sbni_inb(struct sbni_softc *, enum sbni_reg);
12276259Sgreenstatic __inline void	sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
12357429Smarkmstatic __inline void	sbni_insb(struct sbni_softc *, u_char *, u_int);
12457429Smarkmstatic __inline void	sbni_outsb(struct sbni_softc *, u_char *, u_int);
12576259Sgreen
12676259Sgreenstatic u_int32_t crc32tab[];
12757429Smarkm
12892555Sdes#ifdef SBNI_DUAL_COMPOUND
12960573Skrisstatic struct mtx headlist_lock;
13060573SkrisMTX_SYSINIT(headlist_lock, &headlist_lock, "sbni headlist", MTX_DEF);
13176259Sgreenstatic struct sbni_softc *sbni_headlist;
13276259Sgreen#endif
13357429Smarkm
13457429Smarkm/* -------------------------------------------------------------------------- */
13557429Smarkm
13692555Sdesstatic __inline u_char
13776259Sgreensbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
13857429Smarkm{
13957429Smarkm	return bus_space_read_1(
14057429Smarkm	    rman_get_bustag(sc->io_res),
14157429Smarkm	    rman_get_bushandle(sc->io_res),
14257429Smarkm	    sc->io_off + reg);
14357429Smarkm}
14457429Smarkm
14557429Smarkmstatic __inline void
14657429Smarkmsbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
14757429Smarkm{
14892555Sdes	bus_space_write_1(
14976259Sgreen	    rman_get_bustag(sc->io_res),
15057429Smarkm	    rman_get_bushandle(sc->io_res),
15157429Smarkm	    sc->io_off + reg, value);
15257429Smarkm}
15357429Smarkm
15457429Smarkmstatic __inline void
15557429Smarkmsbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
15657429Smarkm{
15757429Smarkm	bus_space_read_multi_1(
15857429Smarkm	    rman_get_bustag(sc->io_res),
15957429Smarkm	    rman_get_bushandle(sc->io_res),
16057429Smarkm	    sc->io_off + DAT, to, len);
16192555Sdes}
16257429Smarkm
16357429Smarkmstatic __inline void
16457429Smarkmsbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
16557429Smarkm{
16657429Smarkm	bus_space_write_multi_1(
16757429Smarkm	    rman_get_bustag(sc->io_res),
16857429Smarkm	    rman_get_bushandle(sc->io_res),
16957429Smarkm	    sc->io_off + DAT, from, len);
17057429Smarkm}
17157429Smarkm
17257429Smarkm
17392555Sdes/*
17457429Smarkm	Valid combinations in CSR0 (for probing):
17557429Smarkm
17692555Sdes	VALID_DECODER	0000,0011,1011,1010
17792555Sdes
17857429Smarkm				    	; 0   ; -
17957429Smarkm				TR_REQ	; 1   ; +
18057429Smarkm			TR_RDY	    	; 2   ; -
18157429Smarkm			TR_RDY	TR_REQ	; 3   ; +
18257429Smarkm		BU_EMP		    	; 4   ; +
18357429Smarkm		BU_EMP	     	TR_REQ	; 5   ; +
18457429Smarkm		BU_EMP	TR_RDY	    	; 6   ; -
18592555Sdes		BU_EMP	TR_RDY	TR_REQ	; 7   ; +
18676259Sgreen	RC_RDY 		     		; 8   ; +
18757429Smarkm	RC_RDY			TR_REQ	; 9   ; +
18857429Smarkm	RC_RDY		TR_RDY		; 10  ; -
18957429Smarkm	RC_RDY		TR_RDY	TR_REQ	; 11  ; -
19057429Smarkm	RC_RDY	BU_EMP			; 12  ; -
19157429Smarkm	RC_RDY	BU_EMP		TR_REQ	; 13  ; -
19257429Smarkm	RC_RDY	BU_EMP	TR_RDY		; 14  ; -
19357429Smarkm	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; -
19457429Smarkm*/
19557429Smarkm
19657429Smarkm#define VALID_DECODER	(2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
19757429Smarkm
19857429Smarkm
19992555Sdesint
20076259Sgreensbni_probe(struct sbni_softc *sc)
20157429Smarkm{
20257429Smarkm	u_char csr0;
20357429Smarkm
20457429Smarkm	csr0 = sbni_inb(sc, CSR0);
20557429Smarkm	if (csr0 != 0xff && csr0 != 0x00) {
20657429Smarkm		csr0 &= ~EN_INT;
20757429Smarkm		if (csr0 & BU_EMP)
20857429Smarkm			csr0 |= EN_INT;
20957429Smarkm
21057429Smarkm		if (VALID_DECODER & (1 << (csr0 >> 4)))
21157429Smarkm			return (0);
21257429Smarkm	}
21357429Smarkm
21457429Smarkm	return (ENXIO);
21557429Smarkm}
21657429Smarkm
21757429Smarkm
21857429Smarkm/*
21957429Smarkm * Install interface into kernel networking data structures
22057429Smarkm */
22157429Smarkmint
22257429Smarkmsbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
22357429Smarkm{
22457429Smarkm	struct ifnet *ifp;
22557429Smarkm	u_char csr0;
22657429Smarkm
22757429Smarkm	ifp = sc->ifp = if_alloc(IFT_ETHER);
22857429Smarkm	if (ifp == NULL)
22957429Smarkm		return (ENOMEM);
23057429Smarkm	sbni_outb(sc, CSR0, 0);
23157429Smarkm	set_initial_values(sc, flags);
23257429Smarkm
23357429Smarkm	/* Initialize ifnet structure */
23457429Smarkm	ifp->if_softc	= sc;
23557429Smarkm	if_initname(ifp, "sbni", unit);
23676259Sgreen	ifp->if_init	= sbni_init;
23757429Smarkm	ifp->if_start	= sbni_start;
23876259Sgreen	ifp->if_ioctl	= sbni_ioctl;
23957429Smarkm	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
24057429Smarkm
24157429Smarkm	/* report real baud rate */
24257429Smarkm	csr0 = sbni_inb(sc, CSR0);
24357429Smarkm	ifp->if_baudrate =
24457429Smarkm		(csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
24557429Smarkm
24657429Smarkm	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
24757429Smarkm
24857429Smarkm	mtx_init(&sc->lock, ifp->if_xname, MTX_NETWORK_LOCK, MTX_DEF);
24957429Smarkm	callout_init_mtx(&sc->wch, &sc->lock, 0);
25057429Smarkm	ether_ifattach(ifp, sc->enaddr);
25192555Sdes	/* device attach does transition from UNCONFIGURED to IDLE state */
25276259Sgreen
25357429Smarkm	if_printf(ifp, "speed %ju, rxl ", (uintmax_t)ifp->if_baudrate);
25476259Sgreen	if (sc->delta_rxl)
25557429Smarkm		printf("auto\n");
25657429Smarkm	else
25757429Smarkm		printf("%d (fixed)\n", sc->cur_rxl_index);
25892555Sdes	return (0);
25957429Smarkm}
26057429Smarkm
26157429Smarkmvoid
26257429Smarkmsbni_detach(struct sbni_softc *sc)
26357429Smarkm{
26457429Smarkm
26557429Smarkm	SBNI_LOCK(sc);
26657429Smarkm	sbni_stop(sc);
26776259Sgreen	SBNI_UNLOCK(sc);
26857429Smarkm	callout_drain(&sc->wch);
26957429Smarkm	ether_ifdetach(sc->ifp);
27057429Smarkm	if (sc->irq_handle)
27157429Smarkm		bus_teardown_intr(sc->dev, sc->irq_res, sc->irq_handle);
27257429Smarkm	mtx_destroy(&sc->lock);
27357429Smarkm	if_free(sc->ifp);
27457429Smarkm}
27557429Smarkm
27657429Smarkmvoid
27757429Smarkmsbni_release_resources(struct sbni_softc *sc)
27857429Smarkm{
27957429Smarkm
28057429Smarkm	if (sc->irq_res)
28157429Smarkm		bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
28257429Smarkm		    sc->irq_res);
28392555Sdes	if (sc->io_res && sc->io_off == 0)
28476259Sgreen		bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->io_rid,
28557429Smarkm		    sc->io_res);
28660573Skris}
28757429Smarkm
28860573Skris/* -------------------------------------------------------------------------- */
28960573Skris
29060573Skrisstatic void
29160573Skrissbni_init(void *xsc)
29257429Smarkm{
29360573Skris	struct sbni_softc *sc;
29460573Skris
29560573Skris	sc = (struct sbni_softc *)xsc;
29669587Sgreen	SBNI_LOCK(sc);
29760573Skris	sbni_init_locked(sc);
29860573Skris	SBNI_UNLOCK(sc);
29960573Skris}
30060573Skris
30160573Skrisstatic void
30260573Skrissbni_init_locked(struct sbni_softc *sc)
30360573Skris{
30460573Skris	struct ifnet *ifp;
30560573Skris
30660573Skris	ifp = sc->ifp;
30760573Skris
30860573Skris	/*
30960573Skris	 * kludge to avoid multiple initialization when more than once
31060573Skris	 * protocols configured
31160573Skris	 */
31257429Smarkm	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
31357429Smarkm		return;
31457429Smarkm
31557429Smarkm	card_start(sc);
31657429Smarkm	callout_reset(&sc->wch, hz/SBNI_HZ, sbni_timeout, sc);
31757429Smarkm
31857429Smarkm	ifp->if_drv_flags |= IFF_DRV_RUNNING;
31957429Smarkm	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
32092555Sdes
32176259Sgreen	/* attempt to start output */
32292555Sdes	sbni_start_locked(ifp);
32357429Smarkm}
32476259Sgreen
32592555Sdesstatic void
32657429Smarkmsbni_start(struct ifnet *ifp)
32760573Skris{
32860573Skris	struct sbni_softc *sc = ifp->if_softc;
32960573Skris
33060573Skris	SBNI_LOCK(sc);
33160573Skris	sbni_start_locked(ifp);
33276259Sgreen	SBNI_UNLOCK(sc);
33360573Skris}
33460573Skris
33560573Skrisstatic void
33660573Skrissbni_start_locked(struct ifnet *ifp)
33760573Skris{
33876259Sgreen	struct sbni_softc *sc = ifp->if_softc;
33960573Skris
34060573Skris	if (sc->tx_frameno == 0)
34160573Skris		prepare_to_send(sc);
34276259Sgreen}
34360573Skris
34476259Sgreen
34560573Skrisstatic void
34692555Sdessbni_stop(struct sbni_softc *sc)
34792555Sdes{
34892555Sdes	sbni_outb(sc, CSR0, 0);
34992555Sdes	drop_xmit_queue(sc);
35092555Sdes
35192555Sdes	if (sc->rx_buf_p) {
35292555Sdes		m_freem(sc->rx_buf_p);
35392555Sdes		sc->rx_buf_p = NULL;
35492555Sdes	}
35592555Sdes
35660573Skris	callout_stop(&sc->wch);
35757429Smarkm	sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
35857429Smarkm}
35957429Smarkm
36076259Sgreen/* -------------------------------------------------------------------------- */
36157429Smarkm
36257429Smarkm/* interrupt handler */
36357429Smarkm
36457429Smarkm/*
36557429Smarkm * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
36657429Smarkm * be looked as two independent single-channel devices. Every channel seems
36757429Smarkm * as Ethernet interface but interrupt handler must be common. Really, first
36857429Smarkm * channel ("master") driver only registers the handler. In it's struct softc
36957429Smarkm * it has got pointer to "slave" channel's struct softc and handles that's
37057429Smarkm * interrupts too.
37176259Sgreen *	softc of successfully attached ISA SBNI boards is linked to list.
37257429Smarkm * While next board driver is initialized, it scans this list. If one
37376259Sgreen * has found softc with same irq and ioaddr different by 4 then it assumes
37476259Sgreen * this board to be "master".
37576259Sgreen */
37676259Sgreen
37776259Sgreenvoid
37876259Sgreensbni_intr(void *arg)
37992555Sdes{
38092555Sdes	struct sbni_softc *sc;
38176259Sgreen	int repeat;
38257429Smarkm
38357429Smarkm	sc = (struct sbni_softc *)arg;
38457429Smarkm
38557429Smarkm	do {
38657429Smarkm		repeat = 0;
38757429Smarkm		SBNI_LOCK(sc);
38857429Smarkm		if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
38957429Smarkm			handle_channel(sc);
39057429Smarkm			repeat = 1;
39192555Sdes		}
39265668Skris		SBNI_UNLOCK(sc);
39357429Smarkm		if (sc->slave_sc) {
39457429Smarkm			/* second channel present */
39557429Smarkm			SBNI_LOCK(sc->slave_sc);
39657429Smarkm			if (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY)) {
39765668Skris				handle_channel(sc->slave_sc);
39865668Skris				repeat = 1;
39965668Skris			}
40065668Skris			SBNI_UNLOCK(sc->slave_sc);
40157429Smarkm		}
40257429Smarkm	} while (repeat);
40357429Smarkm}
40457429Smarkm
40557429Smarkm
40657429Smarkmstatic void
40757429Smarkmhandle_channel(struct sbni_softc *sc)
40865668Skris{
40965668Skris	int req_ans;
41065668Skris	u_char csr0;
41157429Smarkm
41257429Smarkm	sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
41357429Smarkm
41457429Smarkm	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
41557429Smarkm	for (;;) {
41657429Smarkm		csr0 = sbni_inb(sc, CSR0);
41757429Smarkm		if ((csr0 & (RC_RDY | TR_RDY)) == 0)
41857429Smarkm			break;
41957429Smarkm
42057429Smarkm		req_ans = !(sc->state & FL_PREV_OK);
42192555Sdes
42292555Sdes		if (csr0 & RC_RDY)
42392555Sdes			req_ans = recv_frame(sc);
42457429Smarkm
42557429Smarkm		/*
42657429Smarkm		 * TR_RDY always equals 1 here because we have owned the marker,
42765668Skris		 * and we set TR_REQ when disabled interrupts
42865668Skris		 */
42965668Skris		csr0 = sbni_inb(sc, CSR0);
43057429Smarkm		if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
43157429Smarkm			if_printf(sc->ifp, "internal error!\n");
43257429Smarkm
43357429Smarkm		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
43492555Sdes		if (req_ans || sc->tx_frameno != 0)
43560573Skris			send_frame(sc);
43657429Smarkm		else {
43760573Skris			/* send the marker without any data */
43860573Skris			sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
43957429Smarkm		}
44057429Smarkm	}
44157429Smarkm
44257429Smarkm	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
44357429Smarkm}
44457429Smarkm
44557429Smarkm
44657429Smarkm/*
44757429Smarkm * Routine returns 1 if it need to acknoweledge received frame.
44857429Smarkm * Empty frame received without errors won't be acknoweledged.
44957429Smarkm */
45057429Smarkm
45157429Smarkmstatic int
45257429Smarkmrecv_frame(struct sbni_softc *sc)
45357429Smarkm{
45457429Smarkm	u_int32_t crc;
45557429Smarkm	u_int framelen, frameno, ack;
45657429Smarkm	u_int is_first, frame_ok;
45757429Smarkm
45857429Smarkm	crc = CRC32_INITIAL;
45976259Sgreen	if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
46057429Smarkm		frame_ok = framelen > 4 ?
46157429Smarkm		    upload_data(sc, framelen, frameno, is_first, crc) :
46257429Smarkm		    skip_tail(sc, framelen, crc);
46357429Smarkm		if (frame_ok)
46457429Smarkm			interpret_ack(sc, ack);
46557429Smarkm	} else {
46657429Smarkm		framelen = 0;
46757429Smarkm		frame_ok = 0;
46857429Smarkm	}
46957429Smarkm
47057429Smarkm	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
47157429Smarkm	if (frame_ok) {
47260573Skris		sc->state |= FL_PREV_OK;
47360573Skris		if (framelen > 4)
47498675Sdes			sc->in_stats.all_rx_number++;
47598675Sdes	} else {
47698675Sdes		sc->state &= ~FL_PREV_OK;
47798675Sdes		change_level(sc);
47898675Sdes		sc->in_stats.all_rx_number++;
47998675Sdes		sc->in_stats.bad_rx_number++;
48098675Sdes	}
48198675Sdes
48298675Sdes	return (!frame_ok || framelen > 4);
48398675Sdes}
48498675Sdes
48598675Sdes
48698675Sdesstatic void
48798675Sdessend_frame(struct sbni_softc *sc)
48898675Sdes{
48998675Sdes	u_int32_t crc;
49098675Sdes	u_char csr0;
49198675Sdes
49298675Sdes	crc = CRC32_INITIAL;
49398675Sdes	if (sc->state & FL_NEED_RESEND) {
49498675Sdes
49598675Sdes		/* if frame was sended but not ACK'ed - resend it */
49698675Sdes		if (sc->trans_errors) {
49798675Sdes			sc->trans_errors--;
49898675Sdes			if (sc->framelen != 0)
49998675Sdes				sc->in_stats.resend_tx_number++;
50098675Sdes		} else {
50198675Sdes			/* cannot xmit with many attempts */
50298675Sdes			drop_xmit_queue(sc);
50398675Sdes			goto do_send;
50498675Sdes		}
50598675Sdes	} else
50698675Sdes		sc->trans_errors = TR_ERROR_COUNT;
50798675Sdes
50898675Sdes	send_frame_header(sc, &crc);
50998675Sdes	sc->state |= FL_NEED_RESEND;
51098675Sdes	/*
51198675Sdes	 * FL_NEED_RESEND will be cleared after ACK, but if empty
51298675Sdes	 * frame sended then in prepare_to_send next frame
51398675Sdes	 */
51498675Sdes
51598675Sdes
51698675Sdes	if (sc->framelen) {
51798675Sdes		download_data(sc, &crc);
51898675Sdes		sc->in_stats.all_tx_number++;
51998675Sdes		sc->state |= FL_WAIT_ACK;
52098675Sdes	}
52198675Sdes
52298675Sdes	sbni_outsb(sc, (u_char *)&crc, sizeof crc);
52398675Sdes
52498675Sdesdo_send:
52598675Sdes	csr0 = sbni_inb(sc, CSR0);
52698675Sdes	sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
52798675Sdes
52898675Sdes	if (sc->tx_frameno) {
52998675Sdes		/* next frame exists - request to send */
53098675Sdes		sbni_outb(sc, CSR0, csr0 | TR_REQ);
53198675Sdes	}
53298675Sdes}
53398675Sdes
53498675Sdes
53565668Skrisstatic void
53692555Sdesdownload_data(struct sbni_softc *sc, u_int32_t *crc_p)
53765668Skris{
53865668Skris	struct mbuf *m;
53965668Skris	caddr_t	data_p;
54065668Skris	u_int data_len, pos, slice;
54165668Skris
54276259Sgreen	data_p = NULL;		/* initialized to avoid warn */
54376259Sgreen	pos = 0;
54465668Skris
54565668Skris	for (m = sc->tx_buf_p;  m != NULL && pos < sc->pktlen;  m = m->m_next) {
54665668Skris		if (pos + m->m_len > sc->outpos) {
54765668Skris			data_len = m->m_len - (sc->outpos - pos);
54865668Skris			data_p = mtod(m, caddr_t) + (sc->outpos - pos);
54965668Skris
55065668Skris			goto do_copy;
55165668Skris		} else
55265668Skris			pos += m->m_len;
55365668Skris	}
55465668Skris
55565668Skris	data_len = 0;
55665668Skris
55765668Skrisdo_copy:
55865668Skris	pos = 0;
55965668Skris	do {
56065668Skris		if (data_len) {
56165668Skris			slice = min(data_len, sc->framelen - pos);
56265668Skris			sbni_outsb(sc, data_p, slice);
56365668Skris			*crc_p = calc_crc32(*crc_p, data_p, slice);
56465668Skris
56565668Skris			pos += slice;
56665668Skris			if (data_len -= slice)
56765668Skris				data_p += slice;
56865668Skris			else {
56965668Skris				do {
57065668Skris					m = m->m_next;
57165668Skris				} while (m != NULL && m->m_len == 0);
57265668Skris
57365668Skris				if (m) {
57465668Skris					data_len = m->m_len;
57565668Skris					data_p = mtod(m, caddr_t);
57665668Skris				}
57776259Sgreen			}
57876259Sgreen		} else {
57976259Sgreen			/* frame too short - zero padding */
58076259Sgreen
58176259Sgreen			pos = sc->framelen - pos;
58276259Sgreen			while (pos--) {
58376259Sgreen				sbni_outb(sc, DAT, 0);
58476259Sgreen				*crc_p = CRC32(0, *crc_p);
58576259Sgreen			}
58665668Skris			return;
58765668Skris		}
58865668Skris	} while (pos < sc->framelen);
58965668Skris}
59065668Skris
59165668Skris
59265668Skrisstatic int
59365668Skrisupload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
59465668Skris	    u_int is_first, u_int32_t crc)
59565668Skris{
59665668Skris	int frame_ok;
59792555Sdes
59892555Sdes	if (is_first) {
59992555Sdes		sc->wait_frameno = frameno;
60065668Skris		sc->inppos = 0;
60165668Skris	}
60265668Skris
60365668Skris	if (sc->wait_frameno == frameno) {
60465668Skris
60565668Skris		if (sc->inppos + framelen  <=  ETHER_MAX_LEN) {
60665668Skris			frame_ok = append_frame_to_pkt(sc, framelen, crc);
60765668Skris
60865668Skris		/*
60965668Skris		 * if CRC is right but framelen incorrect then transmitter
61065668Skris		 * error was occurred... drop entire packet
61165668Skris		 */
61292555Sdes		} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
61392555Sdes			sc->wait_frameno = 0;
61492555Sdes			sc->inppos = 0;
61592555Sdes			if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
61692555Sdes			/* now skip all frames until is_first != 0 */
61792555Sdes		}
61892555Sdes	} else
61992555Sdes		frame_ok = skip_tail(sc, framelen, crc);
62092555Sdes
62192555Sdes	if (is_first && !frame_ok) {
62292555Sdes		/*
62392555Sdes		 * Frame has been violated, but we have stored
62492555Sdes		 * is_first already... Drop entire packet.
62592555Sdes		 */
62692555Sdes		sc->wait_frameno = 0;
62792555Sdes		if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
62892555Sdes	}
62992555Sdes
63092555Sdes	return (frame_ok);
63192555Sdes}
63292555Sdes
63365668Skris
63465668Skrisstatic __inline void	send_complete(struct sbni_softc *);
63565668Skris
63665668Skrisstatic __inline void
63765668Skrissend_complete(struct sbni_softc *sc)
638106121Sdes{
639106121Sdes	m_freem(sc->tx_buf_p);
640106121Sdes	sc->tx_buf_p = NULL;
641106121Sdes	if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1);
642106121Sdes}
643106121Sdes
644106121Sdes
645106121Sdesstatic void
64665668Skrisinterpret_ack(struct sbni_softc *sc, u_int ack)
647106121Sdes{
648106121Sdes	if (ack == FRAME_SENT_OK) {
649106121Sdes		sc->state &= ~FL_NEED_RESEND;
65065668Skris
65165668Skris		if (sc->state & FL_WAIT_ACK) {
65265668Skris			sc->outpos += sc->framelen;
65365668Skris
65465668Skris			if (--sc->tx_frameno) {
65565668Skris				sc->framelen = min(
65665668Skris				    sc->maxframe, sc->pktlen - sc->outpos);
65765668Skris			} else {
65865668Skris				send_complete(sc);
65965668Skris				prepare_to_send(sc);
66065668Skris			}
66198675Sdes		}
66298675Sdes	}
66398675Sdes
66498675Sdes	sc->state &= ~FL_WAIT_ACK;
66565668Skris}
66665668Skris
66765668Skris
66865668Skris/*
66965668Skris * Glue received frame with previous fragments of packet.
67065668Skris * Indicate packet when last frame would be accepted.
67165668Skris */
67265668Skris
67365668Skrisstatic int
67465668Skrisappend_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
67565668Skris{
67665668Skris	caddr_t p;
67765668Skris
67865668Skris	if (sc->inppos + framelen > ETHER_MAX_LEN)
67965668Skris		return (0);
68065668Skris
68165668Skris	if (!sc->rx_buf_p && !get_rx_buf(sc))
68265668Skris		return (0);
68365668Skris
68465668Skris	p = sc->rx_buf_p->m_data + sc->inppos;
68565668Skris	sbni_insb(sc, p, framelen);
68665668Skris	if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
68765668Skris		return (0);
68865668Skris
68965668Skris	sc->inppos += framelen - 4;
69065668Skris	if (--sc->wait_frameno == 0) {		/* last frame received */
69165668Skris		indicate_pkt(sc);
69265668Skris		if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1);
69365668Skris	}
69465668Skris
69565668Skris	return (1);
69692555Sdes}
69760573Skris
69860573Skris
69960573Skris/*
70065668Skris * Prepare to start output on adapter. Current priority must be set to splimp
70160573Skris * before this routine is called.
70257429Smarkm * Transmitter will be actually activated when marker has been accepted.
70357429Smarkm */
70457429Smarkm
70557429Smarkmstatic void
70676259Sgreenprepare_to_send(struct sbni_softc *sc)
70776259Sgreen{
70857429Smarkm	struct mbuf *m;
70957429Smarkm	u_int len;
71057429Smarkm
71157429Smarkm	/* sc->tx_buf_p == NULL here! */
71257429Smarkm	if (sc->tx_buf_p)
71357429Smarkm		printf("sbni: memory leak!\n");
71457429Smarkm
71557429Smarkm	sc->outpos = 0;
71657429Smarkm	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
71757429Smarkm
71857429Smarkm	for (;;) {
71957429Smarkm		IF_DEQUEUE(&sc->ifp->if_snd, sc->tx_buf_p);
72057429Smarkm		if (!sc->tx_buf_p) {
72157429Smarkm			/* nothing to transmit... */
72257429Smarkm			sc->pktlen     = 0;
72357429Smarkm			sc->tx_frameno = 0;
72457429Smarkm			sc->framelen   = 0;
72557429Smarkm			sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
72657429Smarkm			return;
72757429Smarkm		}
72857429Smarkm
72957429Smarkm		for (len = 0, m = sc->tx_buf_p;  m;  m = m->m_next)
73057429Smarkm			len += m->m_len;
73192555Sdes
73257429Smarkm		if (len != 0)
73357429Smarkm			break;
73457429Smarkm		m_freem(sc->tx_buf_p);
73557429Smarkm	}
73657429Smarkm
73757429Smarkm	if (len < SBNI_MIN_LEN)
73857429Smarkm		len = SBNI_MIN_LEN;
73957429Smarkm
74057429Smarkm	sc->pktlen	= len;
74157429Smarkm	sc->tx_frameno	= howmany(len, sc->maxframe);
74276259Sgreen	sc->framelen	= min(len, sc->maxframe);
74376259Sgreen
74465668Skris	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
74557429Smarkm	sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
74657429Smarkm	BPF_MTAP(sc->ifp, sc->tx_buf_p);
74757429Smarkm}
74857429Smarkm
74992555Sdes
75057429Smarkmstatic void
75157429Smarkmdrop_xmit_queue(struct sbni_softc *sc)
75257429Smarkm{
75357429Smarkm	struct mbuf *m;
75457429Smarkm
75557429Smarkm	if (sc->tx_buf_p) {
75657429Smarkm		m_freem(sc->tx_buf_p);
75757429Smarkm		sc->tx_buf_p = NULL;
75857429Smarkm		if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
75957429Smarkm	}
76057429Smarkm
76176259Sgreen	for (;;) {
76257429Smarkm		IF_DEQUEUE(&sc->ifp->if_snd, m);
76357429Smarkm		if (m == NULL)
76457429Smarkm			break;
76557429Smarkm		m_freem(m);
76657429Smarkm		if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
76757429Smarkm	}
76857429Smarkm
76957429Smarkm	sc->tx_frameno	= 0;
77057429Smarkm	sc->framelen	= 0;
77157429Smarkm	sc->outpos	= 0;
77257429Smarkm	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
77357429Smarkm	sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
77457429Smarkm}
77557429Smarkm
77676259Sgreen
77757429Smarkmstatic void
77857429Smarkmsend_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
77957429Smarkm{
78057429Smarkm	u_int32_t crc;
78157429Smarkm	u_int len_field;
78257429Smarkm	u_char value;
78357429Smarkm
78476259Sgreen	crc = *crc_p;
78557429Smarkm	len_field = sc->framelen + 6;	/* CRC + frameno + reserved */
78657429Smarkm
78757429Smarkm	if (sc->state & FL_NEED_RESEND)
78857429Smarkm		len_field |= FRAME_RETRY;	/* non-first attempt... */
78957429Smarkm
79057429Smarkm	if (sc->outpos == 0)
79157429Smarkm		len_field |= FRAME_FIRST;
79257429Smarkm
79357429Smarkm	len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
79476259Sgreen	sbni_outb(sc, DAT, SBNI_SIG);
79557429Smarkm
79657429Smarkm	value = (u_char)len_field;
79757429Smarkm	sbni_outb(sc, DAT, value);
79857429Smarkm	crc = CRC32(value, crc);
79960573Skris	value = (u_char)(len_field >> 8);
80060573Skris	sbni_outb(sc, DAT, value);
80160573Skris	crc = CRC32(value, crc);
80260573Skris
80360573Skris	sbni_outb(sc, DAT, sc->tx_frameno);
80460573Skris	crc = CRC32(sc->tx_frameno, crc);
80560573Skris	sbni_outb(sc, DAT, 0);
80660573Skris	crc = CRC32(0, crc);
80760573Skris	*crc_p = crc;
80860573Skris}
80960573Skris
81092555Sdes
81176259Sgreen/*
81260573Skris * if frame tail not needed (incorrect number or received twice),
81376259Sgreen * it won't store, but CRC will be calculated
81460573Skris */
81560573Skris
81665668Skrisstatic int
81765668Skrisskip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
81892555Sdes{
81965668Skris	while (tail_len--)
82065668Skris		crc = CRC32(sbni_inb(sc, DAT), crc);
82165668Skris
82265668Skris	return (crc == CRC32_REMAINDER);
82365668Skris}
82465668Skris
82592555Sdes
82676259Sgreenstatic int
82776259Sgreencheck_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
82876259Sgreen	   u_int *ack, u_int *is_first, u_int32_t *crc_p)
82976259Sgreen{
83076259Sgreen	u_int32_t crc;
83192555Sdes	u_char value;
83276259Sgreen
83376259Sgreen	crc = *crc_p;
83476259Sgreen	if (sbni_inb(sc, DAT) != SBNI_SIG)
83576259Sgreen		return (0);
83676259Sgreen
83760573Skris	value = sbni_inb(sc, DAT);
83857429Smarkm	*framelen = (u_int)value;
83957429Smarkm	crc = CRC32(value, crc);
84092555Sdes	value = sbni_inb(sc, DAT);
84192555Sdes	*framelen |= ((u_int)value) << 8;
84257429Smarkm	crc = CRC32(value, crc);
84357429Smarkm
84460573Skris	*ack = *framelen & FRAME_ACK_MASK;
84565668Skris	*is_first = (*framelen & FRAME_FIRST) != 0;
84657429Smarkm
84776259Sgreen	if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
84857429Smarkm		return (0);
84992555Sdes
85057429Smarkm	value = sbni_inb(sc, DAT);
85157429Smarkm	*frameno = (u_int)value;
85257429Smarkm	crc = CRC32(value, crc);
85357429Smarkm
85457429Smarkm	crc = CRC32(sbni_inb(sc, DAT), crc);		/* reserved byte */
85557429Smarkm	*framelen -= 2;
85657429Smarkm
85757429Smarkm	*crc_p = crc;
85857429Smarkm	return (1);
85957429Smarkm}
86057429Smarkm
86157429Smarkm
86257429Smarkmstatic int
86357429Smarkmget_rx_buf(struct sbni_softc *sc)
86476259Sgreen{
86576259Sgreen	struct mbuf *m;
86676259Sgreen
86776259Sgreen	MGETHDR(m, M_NOWAIT, MT_DATA);
86876259Sgreen	if (m == NULL) {
86976259Sgreen		if_printf(sc->ifp, "cannot allocate header mbuf\n");
87076259Sgreen		return (0);
87176259Sgreen	}
87276259Sgreen
87376259Sgreen	/*
87476259Sgreen	 * We always put the received packet in a single buffer -
87576259Sgreen	 * either with just an mbuf header or in a cluster attached
87676259Sgreen	 * to the header. The +2 is to compensate for the alignment
87776259Sgreen	 * fixup below.
87857429Smarkm	 */
87957429Smarkm	if (ETHER_MAX_LEN + 2 > MHLEN) {
88057429Smarkm		/* Attach an mbuf cluster */
88157429Smarkm		if (!(MCLGET(m, M_NOWAIT))) {
88257429Smarkm			m_freem(m);
88357429Smarkm			return (0);
88457429Smarkm		}
88557429Smarkm	}
88657429Smarkm	m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
88757429Smarkm
88857429Smarkm	/*
88960573Skris	 * The +2 is to longword align the start of the real packet.
89060573Skris	 * (sizeof ether_header == 14)
891113908Sdes	 * This is important for NFS.
892113908Sdes	 */
893113908Sdes	m_adj(m, 2);
894113908Sdes	sc->rx_buf_p = m;
895113908Sdes	return (1);
896113908Sdes}
897113908Sdes
898113908Sdes
899113908Sdesstatic void
900113908Sdesindicate_pkt(struct sbni_softc *sc)
90157429Smarkm{
90257429Smarkm	struct ifnet *ifp = sc->ifp;
90357429Smarkm	struct mbuf *m;
90457429Smarkm
90557429Smarkm	m = sc->rx_buf_p;
90657429Smarkm	m->m_pkthdr.rcvif = ifp;
90776259Sgreen	m->m_pkthdr.len   = m->m_len = sc->inppos;
90876259Sgreen	sc->rx_buf_p = NULL;
90992555Sdes
91076259Sgreen	SBNI_UNLOCK(sc);
91176259Sgreen	(*ifp->if_input)(ifp, m);
91276259Sgreen	SBNI_LOCK(sc);
91376259Sgreen}
91476259Sgreen
91576259Sgreen/* -------------------------------------------------------------------------- */
91676259Sgreen
91760573Skris/*
91876259Sgreen * Routine checks periodically wire activity and regenerates marker if
91957429Smarkm * connect was inactive for a long time.
92057429Smarkm */
92157429Smarkm
92257429Smarkmstatic void
92357429Smarkmsbni_timeout(void *xsc)
92457429Smarkm{
92557429Smarkm	struct sbni_softc *sc;
92676259Sgreen	u_char csr0;
92760573Skris
92860573Skris	sc = (struct sbni_softc *)xsc;
92976259Sgreen	SBNI_ASSERT_LOCKED(sc);
93057429Smarkm
93176259Sgreen	csr0 = sbni_inb(sc, CSR0);
93276259Sgreen	if (csr0 & RC_CHK) {
93376259Sgreen
93476259Sgreen		if (sc->timer_ticks) {
93576259Sgreen			if (csr0 & (RC_RDY | BU_EMP))
93676259Sgreen				/* receiving not active */
93776259Sgreen				sc->timer_ticks--;
93876259Sgreen		} else {
93976259Sgreen			sc->in_stats.timeout_number++;
94057429Smarkm			if (sc->delta_rxl)
94176259Sgreen				timeout_change_level(sc);
94276259Sgreen
94376259Sgreen			sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
94476259Sgreen			csr0 = sbni_inb(sc, CSR0);
94576259Sgreen		}
94676259Sgreen	}
94757429Smarkm
94876259Sgreen	sbni_outb(sc, CSR0, csr0 | RC_CHK);
94976259Sgreen	callout_reset(&sc->wch, hz/SBNI_HZ, sbni_timeout, sc);
95076259Sgreen}
95176259Sgreen
95276259Sgreen/* -------------------------------------------------------------------------- */
95357429Smarkm
95476259Sgreenstatic void
95576259Sgreencard_start(struct sbni_softc *sc)
95676259Sgreen{
95757429Smarkm	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
95857429Smarkm	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
95957429Smarkm	sc->state |= FL_PREV_OK;
96057429Smarkm
96192555Sdes	sc->inppos = 0;
96276259Sgreen	sc->wait_frameno = 0;
96392555Sdes
96457429Smarkm	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
96557429Smarkm	sbni_outb(sc, CSR0, EN_INT);
96657429Smarkm}
96757429Smarkm
96876259Sgreen/* -------------------------------------------------------------------------- */
96976259Sgreen
97076259Sgreenstatic u_char rxl_tab[] = {
97157429Smarkm	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
97276259Sgreen	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
97376259Sgreen};
97476259Sgreen
97576259Sgreen#define SIZE_OF_TIMEOUT_RXL_TAB 4
97676259Sgreenstatic u_char timeout_rxl_tab[] = {
97776259Sgreen	0x03, 0x05, 0x08, 0x0b
97876259Sgreen};
97976259Sgreen
98060573Skrisstatic void
98176259Sgreenset_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
98257429Smarkm{
98360573Skris	if (flags.fixed_rxl) {
98460573Skris		sc->delta_rxl = 0; /* disable receive level autodetection */
98557429Smarkm		sc->cur_rxl_index = flags.rxl;
98660573Skris	} else {
98760573Skris		sc->delta_rxl = DEF_RXL_DELTA;
98876259Sgreen		sc->cur_rxl_index = DEF_RXL;
98960573Skris	}
99060573Skris
99160573Skris	sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
99260573Skris	sc->csr1.rxl  = rxl_tab[sc->cur_rxl_index];
99376259Sgreen	sc->maxframe  = DEFAULT_FRAME_LEN;
99460573Skris
99560573Skris	/*
99657429Smarkm	 * generate Ethernet address (0x00ff01xxxxxx)
99776259Sgreen	 */
99857429Smarkm	*(u_int16_t *) sc->enaddr = htons(0x00ff);
99957429Smarkm	if (flags.mac_addr) {
100076259Sgreen		*(u_int32_t *) (sc->enaddr + 2) =
100176259Sgreen		    htonl(flags.mac_addr | 0x01000000);
100276259Sgreen	} else {
100376259Sgreen		*(u_char *) (sc->enaddr + 2) = 0x01;
100457429Smarkm		read_random(sc->enaddr + 3, 3);
100557429Smarkm	}
100657429Smarkm}
100757429Smarkm
100857429Smarkm
100957429Smarkm#ifdef SBNI_DUAL_COMPOUND
101057429Smarkmvoid
101192555Sdessbni_add(struct sbni_softc *sc)
101257429Smarkm{
101392555Sdes
101492555Sdes	mtx_lock(&headlist_lock);
101592555Sdes	sc->link = sbni_headlist;
101692555Sdes	sbni_headlist = sc;
101792555Sdes	mtx_unlock(&headlist_lock);
101892555Sdes}
101992555Sdes
102092555Sdesstruct sbni_softc *
102192555Sdesconnect_to_master(struct sbni_softc *sc)
102292555Sdes{
102392555Sdes	struct sbni_softc *p, *p_prev;
102492555Sdes
102592555Sdes	mtx_lock(&headlist_lock);
102692555Sdes	for (p = sbni_headlist, p_prev = NULL; p; p_prev = p, p = p->link) {
102792555Sdes		if (rman_get_start(p->io_res) == rman_get_start(sc->io_res) + 4 ||
102892555Sdes		    rman_get_start(p->io_res) == rman_get_start(sc->io_res) - 4) {
102992555Sdes			p->slave_sc = sc;
103057429Smarkm			if (p_prev)
103157429Smarkm				p_prev->link = p->link;
103257429Smarkm			else
103357429Smarkm				sbni_headlist = p->link;
103457429Smarkm			mtx_unlock(&headlist_lock);
103557429Smarkm			return p;
103657429Smarkm		}
103757429Smarkm	}
103892555Sdes	mtx_unlock(&headlist_lock);
103957429Smarkm
104057429Smarkm	return (NULL);
104157429Smarkm}
104257429Smarkm
104357429Smarkm#endif	/* SBNI_DUAL_COMPOUND */
104457429Smarkm
104557429Smarkm
104657429Smarkm/* Receive level auto-selection */
104757429Smarkm
104876259Sgreenstatic void
104957429Smarkmchange_level(struct sbni_softc *sc)
105057429Smarkm{
105157429Smarkm	if (sc->delta_rxl == 0)		/* do not auto-negotiate RxL */
105257429Smarkm		return;
105357429Smarkm
105457429Smarkm	if (sc->cur_rxl_index == 0)
105557429Smarkm		sc->delta_rxl = 1;
105657429Smarkm	else if (sc->cur_rxl_index == 15)
105757429Smarkm		sc->delta_rxl = -1;
105857429Smarkm	else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
105957429Smarkm		sc->delta_rxl = -sc->delta_rxl;
106076259Sgreen
106157429Smarkm	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
106257429Smarkm	sbni_inb(sc, CSR0);	/* it needed for PCI cards */
106357429Smarkm	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
106457429Smarkm
106557429Smarkm	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
106657429Smarkm	sc->cur_rxl_rcvd  = 0;
106757429Smarkm}
106857429Smarkm
106957429Smarkm
107057429Smarkmstatic void
107157429Smarkmtimeout_change_level(struct sbni_softc *sc)
107292555Sdes{
107357429Smarkm	sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
107457429Smarkm	if (++sc->timeout_rxl >= 4)
107592555Sdes		sc->timeout_rxl = 0;
107692555Sdes
107757429Smarkm	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
107857429Smarkm	sbni_inb(sc, CSR0);
107957429Smarkm	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
108057429Smarkm
108157429Smarkm	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
108260573Skris	sc->cur_rxl_rcvd  = 0;
108360573Skris}
108460573Skris
108592555Sdes/* -------------------------------------------------------------------------- */
108692555Sdes
108760573Skris/*
108876259Sgreen * Process an ioctl request. This code needs some work - it looks
108960573Skris *	pretty ugly.
109092555Sdes */
109160573Skris
109260573Skrisstatic int
109360573Skrissbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
109460573Skris{
109592555Sdes	struct sbni_softc *sc;
109692555Sdes	struct ifreq *ifr;
109760573Skris	struct thread *td;
109876259Sgreen	struct sbni_in_stats *in_stats;
109960573Skris	struct sbni_flags flags;
110092555Sdes	int error;
110160573Skris
110260573Skris	sc = ifp->if_softc;
110360573Skris	ifr = (struct ifreq *)data;
110460573Skris	td = curthread;
110592555Sdes	error = 0;
110692555Sdes
110760573Skris	switch (command) {
110860573Skris	case SIOCSIFFLAGS:
110992555Sdes		/*
111060573Skris		 * If the interface is marked up and stopped, then start it.
111160573Skris		 * If it is marked down and running, then stop it.
111260573Skris		 */
111360573Skris		SBNI_LOCK(sc);
111460573Skris		if (ifp->if_flags & IFF_UP) {
111560573Skris			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
111660573Skris				sbni_init_locked(sc);
111760573Skris		} else {
111860573Skris			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
111960573Skris				sbni_stop(sc);
112060573Skris			}
112160573Skris		}
112292555Sdes		SBNI_UNLOCK(sc);
112376259Sgreen		break;
112476259Sgreen
1125106121Sdes	case SIOCADDMULTI:
112676259Sgreen	case SIOCDELMULTI:
112776259Sgreen		/*
112892555Sdes		 * Multicast list has changed; set the hardware filter
112976259Sgreen		 * accordingly.
113076259Sgreen		 */
113176259Sgreen		error = 0;
113276259Sgreen		/* if (ifr == NULL)
113376259Sgreen			error = EAFNOSUPPORT; */
113476259Sgreen		break;
113592555Sdes
113676259Sgreen		/*
113776259Sgreen		 * SBNI specific ioctl
113876259Sgreen		 */
113976259Sgreen	case SIOCGHWFLAGS:	/* get flags */
114092555Sdes		SBNI_LOCK(sc);
114192555Sdes		bcopy((caddr_t)IF_LLADDR(sc->ifp)+3, (caddr_t) &flags, 3);
114292555Sdes		flags.rxl = sc->cur_rxl_index;
114392555Sdes		flags.rate = sc->csr1.rate;
114492555Sdes		flags.fixed_rxl = (sc->delta_rxl == 0);
114576259Sgreen		flags.fixed_rate = 1;
114692555Sdes		SBNI_UNLOCK(sc);
114792555Sdes		bcopy(&flags, &ifr->ifr_ifru, sizeof(flags));
114892555Sdes		break;
114992555Sdes
115076259Sgreen	case SIOCGINSTATS:
115176259Sgreen		in_stats = malloc(sizeof(struct sbni_in_stats), M_DEVBUF,
115276259Sgreen		    M_WAITOK);
115376259Sgreen		SBNI_LOCK(sc);
115476259Sgreen		bcopy(&sc->in_stats, in_stats, sizeof(struct sbni_in_stats));
1155106121Sdes		SBNI_UNLOCK(sc);
115676259Sgreen		error = copyout(in_stats, ifr_data_get_ptr(ifr),
115776259Sgreen		    sizeof(struct sbni_in_stats));
115876259Sgreen		free(in_stats, M_DEVBUF);
115976259Sgreen		break;
116076259Sgreen
116192555Sdes	case SIOCSHWFLAGS:	/* set flags */
116276259Sgreen		/* root only */
116376259Sgreen		error = priv_check(td, PRIV_DRIVER);
116476259Sgreen		if (error)
116576259Sgreen			break;
116676259Sgreen		bcopy(&ifr->ifr_ifru, &flags, sizeof(flags));
116776259Sgreen		SBNI_LOCK(sc);
116876259Sgreen		if (flags.fixed_rxl) {
116976259Sgreen			sc->delta_rxl = 0;
117076259Sgreen			sc->cur_rxl_index = flags.rxl;
117176259Sgreen		} else {
117276259Sgreen			sc->delta_rxl = DEF_RXL_DELTA;
117376259Sgreen			sc->cur_rxl_index = DEF_RXL;
117476259Sgreen		}
117592555Sdes		sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
117676259Sgreen		sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
117776259Sgreen		if (flags.mac_addr)
117876259Sgreen			bcopy((caddr_t) &flags,
117992555Sdes			      (caddr_t) IF_LLADDR(sc->ifp)+3, 3);
118076259Sgreen
118192555Sdes		/* Don't be afraid... */
118292555Sdes		sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
118392555Sdes		SBNI_UNLOCK(sc);
118492555Sdes		break;
118592555Sdes
118692555Sdes	case SIOCRINSTATS:
118792555Sdes		SBNI_LOCK(sc);
118876259Sgreen		if (!(error = priv_check(td, PRIV_DRIVER)))	/* root only */
118976259Sgreen			bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
119076259Sgreen		SBNI_UNLOCK(sc);
1191106121Sdes		break;
119276259Sgreen
119376259Sgreen	default:
119476259Sgreen		error = ether_ioctl(ifp, command, data);
119592555Sdes		break;
119676259Sgreen	}
119776259Sgreen
119876259Sgreen	return (error);
119976259Sgreen}
120076259Sgreen
120176259Sgreen/* -------------------------------------------------------------------------- */
120276259Sgreen
120392555Sdesstatic u_int32_t
120492555Sdescalc_crc32(u_int32_t crc, caddr_t p, u_int len)
120592555Sdes{
120692555Sdes	while (len--)
120792555Sdes		crc = CRC32(*p++, crc);
120892555Sdes
120992555Sdes	return (crc);
121076259Sgreen}
121176259Sgreen
121276259Sgreenstatic u_int32_t crc32tab[] __aligned(8) = {
121360573Skris	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
121492555Sdes	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
121592555Sdes	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
121660573Skris	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
121760573Skris	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
121860573Skris	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
121960573Skris	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
122099060Sdes	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
122160573Skris	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
122260573Skris	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
122360573Skris	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
122460573Skris	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
122560573Skris	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
122660573Skris	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
122760573Skris	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
122860573Skris	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
122960573Skris	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
123076259Sgreen	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
123176259Sgreen	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
123276259Sgreen	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
123376259Sgreen	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
123476259Sgreen	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
123576259Sgreen	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
123660573Skris	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
123760573Skris	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
123860573Skris	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
123960573Skris	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
124060573Skris	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
124160573Skris	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
124260573Skris	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
124392555Sdes	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
124492555Sdes	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
124592555Sdes	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
124692555Sdes	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
124792555Sdes	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
124892555Sdes	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
124992555Sdes	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
125092555Sdes	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
125160573Skris	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
125260573Skris	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
125360573Skris	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
125460573Skris	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
125560573Skris	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
125692555Sdes	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
125792555Sdes	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
125892555Sdes	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
125992555Sdes	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
126060573Skris	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
126160573Skris	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
126260573Skris	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
126360573Skris	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
126492555Sdes	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
126592555Sdes	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
126676259Sgreen	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
126776259Sgreen	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
126876259Sgreen	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
126976259Sgreen	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
127060573Skris	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
127176259Sgreen	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
127276259Sgreen	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
127376259Sgreen	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
127476259Sgreen	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
127576259Sgreen	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
127676259Sgreen	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
127776259Sgreen};
127876259Sgreen