From ea38b939e8d945b53c579e04a0b3bb9b32eec0c5 Mon Sep 17 00:00:00 2001
From: Max Khon <fjoe@FreeBSD.org>
Date: Wed, 21 Nov 2001 22:29:35 +0000
Subject: [PATCH] Add driver for Granch SBNI12-xx ISA and PCI network adapters.

MFC after:	1 week
---
 sys/conf/NOTES             |    6 +
 sys/conf/files.i386        |    3 +
 sys/dev/sbni/if_sbni.c     | 1312 ++++++++++++++++++++++++++++++++++++
 sys/dev/sbni/if_sbni_isa.c |  165 +++++
 sys/dev/sbni/if_sbni_pci.c |  163 +++++
 sys/dev/sbni/if_sbnireg.h  |  106 +++
 sys/dev/sbni/if_sbnivar.h  |  152 +++++
 sys/i386/conf/NOTES        |    6 +
 sys/modules/Makefile       |    1 +
 sys/modules/sbni/Makefile  |   10 +
 10 files changed, 1924 insertions(+)
 create mode 100644 sys/dev/sbni/if_sbni.c
 create mode 100644 sys/dev/sbni/if_sbni_isa.c
 create mode 100644 sys/dev/sbni/if_sbni_pci.c
 create mode 100644 sys/dev/sbni/if_sbnireg.h
 create mode 100644 sys/dev/sbni/if_sbnivar.h
 create mode 100644 sys/modules/sbni/Makefile

diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index e56a06e87652..3a436c08b5aa 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -1723,6 +1723,7 @@ device		miibus
 #       Olicom PCI token-ring adapters OC-3136, OC-3137, OC-3139, OC-3140,
 #       OC-3141, OC-3540, OC-3250
 # rdp:  RealTek RTL 8002-based pocket ethernet adapters
+# sbni:	Granch SBNI12-xx ISA and PCI adapters
 # pcn:	Support for PCI fast ethernet adapters based on the AMD Am79c97x
 #	chipsets, including the PCnet/FAST, PCnet/FAST+, PCnet/PRO and
 #	PCnet/Home. These were previously handled by the lnc driver (and
@@ -1841,6 +1842,11 @@ hint.rdp.0.at="isa"
 hint.rdp.0.port="0x378"
 hint.rdp.0.irq="7"
 hint.rdp.0.flags="2"
+device		sbni	1
+hint.sbni.0.at="isa"
+hint.sbni.0.port="0x210"
+hint.sbni.0.irq="0xefdead"
+hint.sbni.0.flags="0"
 device		sr	1
 hint.sr.0.at="isa"
 hint.sr.0.port="0x300"
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 5b8796b4a29c..f171d77e08bc 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -111,6 +111,9 @@ dev/kbd/kbd.c			optional	sc
 dev/kbd/kbd.c			optional	ukbd
 dev/kbd/kbd.c			optional	vt
 dev/lnc/if_lnc_isa.c		optional	lnc isa
+dev/sbni/if_sbni.c		optional	sbni
+dev/sbni/if_sbni_isa.c		optional	sbni isa
+dev/sbni/if_sbni_pci.c		optional	sbni pci
 dev/sio/sio.c			optional	sio
 dev/sio/sio_isa.c		optional	sio isa
 dev/sio/sio_pccard.c		optional	sio card
diff --git a/sys/dev/sbni/if_sbni.c b/sys/dev/sbni/if_sbni.c
new file mode 100644
index 000000000000..fd06be34db58
--- /dev/null
+++ b/sys/dev/sbni/if_sbni.c
@@ -0,0 +1,1312 @@
+/*
+ * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
+ * Author: Denis I.Timofeev <timofeev@granch.ru>
+ *
+ * Redistributon and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Device driver for Granch SBNI12 leased line adapters
+ *
+ * Revision 2.0.0  1997/08/06
+ * Initial revision by Alexey Zverev
+ *
+ * Revision 2.0.1 1997/08/11
+ * Additional internal statistics support (tx statistics)
+ *
+ * Revision 2.0.2 1997/11/05
+ * if_bpf bug has been fixed
+ *
+ * Revision 2.0.3 1998/12/20
+ * Memory leakage has been eliminated in
+ * the sbni_st and sbni_timeout routines.
+ *
+ * Revision 3.0 2000/08/10 by Yaroslav Polyakov
+ * Support for PCI cards. 4.1 modification.
+ *
+ * Revision 3.1 2000/09/12
+ * Removed extra #defines around bpf functions
+ *
+ * Revision 4.0 2000/11/23 by Denis Timofeev
+ * Completely redesigned the buffer management
+ *
+ * Revision 4.1 2001/01/21
+ * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
+ *
+ * Written with reference to NE2000 driver developed by David Greenman.
+ */
+ 
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/callout.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+#include <net/bpf.h>
+
+#include <dev/sbni/if_sbnireg.h>
+#include <dev/sbni/if_sbnivar.h>
+
+#define ASM_CRC 1
+
+static void	sbni_init(void *);
+static void	sbni_start(struct ifnet *);
+static int	sbni_ioctl(struct ifnet *, u_long, caddr_t);
+static void	sbni_watchdog(struct ifnet *);
+static void	sbni_stop(struct sbni_softc *);
+static void	handle_channel(struct sbni_softc *);
+
+static void	card_start(struct sbni_softc *);
+static int	recv_frame(struct sbni_softc *);
+static void	send_frame(struct sbni_softc *);
+static int	upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
+static int	skip_tail(struct sbni_softc *, u_int, u_int32_t);
+static void	interpret_ack(struct sbni_softc *, u_int);
+static void	download_data(struct sbni_softc *, u_int32_t *);
+static void	prepare_to_send(struct sbni_softc *);
+static void	drop_xmit_queue(struct sbni_softc *);
+static int	get_rx_buf(struct sbni_softc *);
+static void	indicate_pkt(struct sbni_softc *);
+static void	change_level(struct sbni_softc *);
+static int	check_fhdr(struct sbni_softc *, u_int *, u_int *,
+			   u_int *, u_int *, u_int32_t *); 
+static int	append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
+static void	timeout_change_level(struct sbni_softc *);
+static void	send_frame_header(struct sbni_softc *, u_int32_t *);
+static void	set_initial_values(struct sbni_softc *, struct sbni_flags);
+
+static u_int32_t	calc_crc32(u_int32_t, caddr_t, u_int);
+static timeout_t	sbni_timeout;
+
+static __inline u_char	sbni_inb(struct sbni_softc *, enum sbni_reg);
+static __inline void	sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
+static __inline void	sbni_insb(struct sbni_softc *, u_char *, u_int);
+static __inline void	sbni_outsb(struct sbni_softc *, u_char *, u_int);
+
+static u_int32_t crc32tab[];
+
+#ifdef SBNI_DUAL_COMPOUND
+struct sbni_softc *headlist;
+#endif
+
+u_int32_t next_sbni_unit;
+
+/* -------------------------------------------------------------------------- */
+
+static __inline u_char
+sbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
+{
+	return (inb(sc->base_addr + reg));
+}
+
+static __inline void
+sbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
+{
+	outb(sc->base_addr + reg, value);
+}
+
+static __inline void
+sbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
+{
+	insb(sc->base_addr + DAT, to, len);
+}
+
+static __inline void
+sbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
+{
+	outsb(sc->base_addr + DAT, from, len);
+}
+
+
+/*
+	Valid combinations in CSR0 (for probing):
+
+	VALID_DECODER	0000,0011,1011,1010
+
+				    	; 0   ; -
+				TR_REQ	; 1   ; +
+			TR_RDY	    	; 2   ; -
+			TR_RDY	TR_REQ	; 3   ; +
+		BU_EMP		    	; 4   ; +
+		BU_EMP	     	TR_REQ	; 5   ; +
+		BU_EMP	TR_RDY	    	; 6   ; -
+		BU_EMP	TR_RDY	TR_REQ	; 7   ; +
+	RC_RDY 		     		; 8   ; +
+	RC_RDY			TR_REQ	; 9   ; +
+	RC_RDY		TR_RDY		; 10  ; -
+	RC_RDY		TR_RDY	TR_REQ	; 11  ; -
+	RC_RDY	BU_EMP			; 12  ; -
+	RC_RDY	BU_EMP		TR_REQ	; 13  ; -
+	RC_RDY	BU_EMP	TR_RDY		; 14  ; -
+	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; -
+*/
+
+#define VALID_DECODER	(2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
+
+
+int
+sbni_probe(struct sbni_softc *sc)
+{
+	u_char csr0;
+
+	csr0 = sbni_inb(sc, CSR0);
+	if (csr0 != 0xff && csr0 != 0x00) {
+		csr0 &= ~EN_INT;
+		if (csr0 & BU_EMP)
+			csr0 |= EN_INT;
+      
+		if (VALID_DECODER & (1 << (csr0 >> 4)))
+			return (0);
+	}
+   
+	return (ENXIO);
+}
+
+
+/*
+ * Install interface into kernel networking data structures
+ */
+void
+sbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
+{
+	struct ifnet *ifp;
+	u_char csr0;
+   
+	ifp = &sc->arpcom.ac_if;
+	sbni_outb(sc, CSR0, 0);
+	set_initial_values(sc, flags);
+
+	callout_handle_init(&sc->wch);
+	if (!ifp->if_name) {
+		/* Initialize ifnet structure */
+		ifp->if_softc	= sc;
+		ifp->if_unit	= unit;
+		ifp->if_name	= "sbni";
+		ifp->if_init	= sbni_init;
+		ifp->if_start	= sbni_start;
+	        ifp->if_output	= ether_output;
+		ifp->if_ioctl	= sbni_ioctl;
+		ifp->if_watchdog	= sbni_watchdog;
+		ifp->if_snd.ifq_maxlen	= IFQ_MAXLEN;
+
+		/* report real baud rate */
+		csr0 = sbni_inb(sc, CSR0);
+		ifp->if_baudrate =
+			(csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
+
+	        ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+		ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
+	}
+	/* device attach does transition from UNCONFIGURED to IDLE state */
+
+	printf("%s%d: speed %ld, address %6D, rxl ", ifp->if_name,
+	       ifp->if_unit, ifp->if_baudrate, sc->arpcom.ac_enaddr, ":");
+	if (sc->delta_rxl)
+		printf("auto\n");
+	else
+		printf("%d (fixed)\n", sc->cur_rxl_index);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void
+sbni_init(void *xsc)
+{
+	struct sbni_softc *sc;
+	struct ifnet *ifp;
+	int  s;
+
+	sc = (struct sbni_softc *)xsc;
+	ifp = &sc->arpcom.ac_if;
+
+	/* address not known */
+	if (TAILQ_EMPTY(&ifp->if_addrhead))
+		return;
+
+	/*
+	 * kludge to avoid multiple initialization when more than once
+	 * protocols configured
+	 */
+	if (ifp->if_flags & IFF_RUNNING)
+		return;
+
+	s = splimp();
+	ifp->if_timer = 0;
+	card_start(sc);
+	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
+
+	ifp->if_flags |= IFF_RUNNING;
+	ifp->if_flags &= ~IFF_OACTIVE;
+
+	/* attempt to start output */
+	sbni_start(ifp);
+	splx(s);
+}
+
+
+static void
+sbni_start(struct ifnet *ifp)
+{
+	struct sbni_softc *sc = ifp->if_softc;
+	if (sc->tx_frameno == 0)
+		prepare_to_send(sc);
+}
+
+
+static void
+sbni_stop(struct sbni_softc *sc)
+{
+	sbni_outb(sc, CSR0, 0);
+	drop_xmit_queue(sc);
+
+	if (sc->rx_buf_p) {
+		m_freem(sc->rx_buf_p);
+		sc->rx_buf_p = NULL;
+	}
+
+	untimeout(sbni_timeout, sc, sc->wch);
+	sc->wch.callout = NULL;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* interrupt handler */
+
+/*
+ * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
+ * be looked as two independent single-channel devices. Every channel seems
+ * as Ethernet interface but interrupt handler must be common. Really, first
+ * channel ("master") driver only registers the handler. In it's struct softc
+ * it has got pointer to "slave" channel's struct softc and handles that's
+ * interrupts too.
+ *	softc of successfully attached ISA SBNI boards is linked to list.
+ * While next board driver is initialized, it scans this list. If one
+ * has found softc with same irq and ioaddr different by 4 then it assumes
+ * this board to be "master".
+ */ 
+
+void
+sbni_intr(void *arg)
+{
+	struct sbni_softc *sc;
+	int repeat;
+
+	sc = (struct sbni_softc *)arg;
+
+	do {
+		repeat = 0;
+		if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
+			handle_channel(sc);
+			repeat = 1;
+		}
+		if (sc->slave_sc 	/* second channel present */
+		    && (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY))) {
+			handle_channel(sc->slave_sc);
+			repeat = 1;
+		}
+	} while (repeat);
+}
+
+
+static void
+handle_channel(struct sbni_softc *sc)
+{
+	int req_ans;
+	u_char csr0;
+
+	sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
+
+	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
+	for (;;) {
+		csr0 = sbni_inb(sc, CSR0);
+		if ((csr0 & (RC_RDY | TR_RDY)) == 0)
+			break;
+
+		req_ans = !(sc->state & FL_PREV_OK);
+
+		if (csr0 & RC_RDY)
+			req_ans = recv_frame(sc);
+
+		/*
+		 * TR_RDY always equals 1 here because we have owned the marker,
+		 * and we set TR_REQ when disabled interrupts
+		 */
+		csr0 = sbni_inb(sc, CSR0);
+		if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
+			printf("sbni: internal error!\n");
+
+		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
+		if (req_ans || sc->tx_frameno != 0)
+			send_frame(sc);
+		else
+			/* send the marker without any data */
+			sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
+	}
+
+	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
+}
+
+
+/*
+ * Routine returns 1 if it need to acknoweledge received frame.
+ * Empty frame received without errors won't be acknoweledged.
+ */
+
+static int
+recv_frame(struct sbni_softc *sc)
+{
+	u_int32_t crc;
+	u_int framelen, frameno, ack;
+	u_int is_first, frame_ok;
+
+	crc = CRC32_INITIAL;
+	if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
+		frame_ok = framelen > 4
+			   ? upload_data(sc, framelen, frameno, is_first, crc)
+			   : skip_tail(sc, framelen, crc);
+		if (frame_ok)
+			interpret_ack(sc, ack);
+	} else
+		frame_ok = 0;
+
+	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
+	if (frame_ok) {
+		sc->state |= FL_PREV_OK;
+		if (framelen > 4)
+			sc->in_stats.all_rx_number++;
+	} else {
+		sc->state &= ~FL_PREV_OK;
+		change_level(sc);
+		sc->in_stats.all_rx_number++;
+		sc->in_stats.bad_rx_number++;
+	}
+
+	return (!frame_ok || framelen > 4);
+}
+
+
+static void
+send_frame(struct sbni_softc *sc)
+{
+	u_int32_t crc;
+	u_char csr0;
+
+	crc = CRC32_INITIAL;
+	if (sc->state & FL_NEED_RESEND) {
+
+		/* if frame was sended but not ACK'ed - resend it */
+		if (sc->trans_errors) {
+			sc->trans_errors--;
+			if (sc->framelen != 0)
+				sc->in_stats.resend_tx_number++;
+		} else {
+			/* cannot xmit with many attempts */
+			drop_xmit_queue(sc);
+			goto do_send;
+		}
+	} else
+		sc->trans_errors = TR_ERROR_COUNT;
+
+	send_frame_header(sc, &crc);
+	sc->state |= FL_NEED_RESEND;
+	/*
+	 * FL_NEED_RESEND will be cleared after ACK, but if empty
+	 * frame sended then in prepare_to_send next frame
+	 */
+
+
+	if (sc->framelen) {
+		download_data(sc, &crc);
+		sc->in_stats.all_tx_number++;
+		sc->state |= FL_WAIT_ACK;
+	}
+
+	sbni_outsb(sc, (u_char *)&crc, sizeof crc);
+
+do_send:
+	csr0 = sbni_inb(sc, CSR0);
+	sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
+
+	if (sc->tx_frameno) {
+		/* next frame exists - request to send */
+		sbni_outb(sc, CSR0, csr0 | TR_REQ);
+	}
+}
+
+
+static void
+download_data(struct sbni_softc *sc, u_int32_t *crc_p)
+{
+	struct mbuf *m;
+	caddr_t	data_p;
+	u_int data_len, pos, slice;
+
+	data_p = NULL;		/* initialized to avoid warn */
+	pos = 0;
+
+	for (m = sc->tx_buf_p;  m != NULL && pos < sc->pktlen;  m = m->m_next) {
+		if (pos + m->m_len > sc->outpos) {
+			data_len = m->m_len - (sc->outpos - pos);
+			data_p = mtod(m, caddr_t) + (sc->outpos - pos);
+
+			goto do_copy;
+		} else
+			pos += m->m_len;
+	}
+
+	data_len = 0;
+
+do_copy:
+	pos = 0;
+	do {
+		if (data_len) {
+			slice = min(data_len, sc->framelen - pos);
+			sbni_outsb(sc, data_p, slice);
+			*crc_p = calc_crc32(*crc_p, data_p, slice);
+
+			pos += slice;
+			if (data_len -= slice)
+				data_p += slice;
+			else {
+				do	m = m->m_next;
+				while (m != NULL && m->m_len == 0);
+
+				if (m) {
+					data_len = m->m_len;
+					data_p = mtod(m, caddr_t);
+				}
+			}
+		} else {
+			/* frame too short - zero padding */
+
+			pos = sc->framelen - pos;
+			while (pos--) {
+				sbni_outb(sc, DAT, 0);
+				*crc_p = CRC32(0, *crc_p);
+			}
+			return;
+		}
+	} while (pos < sc->framelen);
+}
+
+
+static int
+upload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
+	    u_int is_first, u_int32_t crc)
+{
+	int frame_ok;
+
+	if (is_first) {
+		sc->wait_frameno = frameno;
+		sc->inppos = 0;
+	}
+
+	if (sc->wait_frameno == frameno) {
+
+		if (sc->inppos + framelen  <=  ETHER_MAX_LEN) {
+			frame_ok = append_frame_to_pkt(sc, framelen, crc);
+
+		/*
+		 * if CRC is right but framelen incorrect then transmitter
+		 * error was occured... drop entire packet
+		 */
+		} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
+			sc->wait_frameno = 0;
+			sc->inppos = 0;
+			sc->arpcom.ac_if.if_ierrors++;
+			/* now skip all frames until is_first != 0 */
+		}
+	} else
+		frame_ok = skip_tail(sc, framelen, crc);
+
+	if (is_first && !frame_ok) {
+		/*
+		 * Frame has been violated, but we have stored
+		 * is_first already... Drop entire packet.
+		 */
+		sc->wait_frameno = 0;
+		sc->arpcom.ac_if.if_ierrors++;
+	}
+
+	return (frame_ok);
+}
+
+
+static __inline void	send_complete(struct sbni_softc *);
+
+static __inline void
+send_complete(struct sbni_softc *sc)
+{
+	m_freem(sc->tx_buf_p);
+	sc->tx_buf_p = NULL;
+	sc->arpcom.ac_if.if_opackets++;
+}
+
+
+static void
+interpret_ack(struct sbni_softc *sc, u_int ack)
+{
+	if (ack == FRAME_SENT_OK) {
+		sc->state &= ~FL_NEED_RESEND;
+
+		if (sc->state & FL_WAIT_ACK) {
+			sc->outpos += sc->framelen;
+
+			if (--sc->tx_frameno)
+				sc->framelen = min(sc->maxframe,
+						   sc->pktlen - sc->outpos);
+			else {
+				send_complete(sc);
+				prepare_to_send(sc);
+			}
+		}
+	}
+
+	sc->state &= ~FL_WAIT_ACK;
+}
+
+
+/*
+ * Glue received frame with previous fragments of packet.
+ * Indicate packet when last frame would be accepted.
+ */
+
+static int
+append_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
+{
+	caddr_t p;
+
+	if (sc->inppos + framelen > ETHER_MAX_LEN)
+		return (0);
+
+	if (!sc->rx_buf_p && !get_rx_buf(sc))
+		return (0);
+
+	p = sc->rx_buf_p->m_data + sc->inppos;
+	sbni_insb(sc, p, framelen);
+	if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
+		return (0);
+
+	sc->inppos += framelen - 4;
+	if (--sc->wait_frameno == 0) {		/* last frame received */
+		indicate_pkt(sc);
+		sc->arpcom.ac_if.if_ipackets++;
+	}
+
+	return (1);
+}
+
+
+/*
+ * Prepare to start output on adapter. Current priority must be set to splimp
+ * before this routine is called.
+ * Transmitter will be actually activated when marker has been accepted.
+ */
+
+static void
+prepare_to_send(struct sbni_softc *sc)
+{
+	struct mbuf *m;
+	u_int len;
+
+	/* sc->tx_buf_p == NULL here! */
+	if (sc->tx_buf_p)
+		printf("sbni: memory leak!\n");
+
+	sc->outpos = 0;
+	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
+
+	for (;;) {
+		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, sc->tx_buf_p);
+		if (!sc->tx_buf_p) {
+			/* nothing to transmit... */
+			sc->pktlen     = 0;
+			sc->tx_frameno = 0;
+			sc->framelen   = 0;
+			sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+			return;
+		}
+
+		for (len = 0, m = sc->tx_buf_p;  m;  m = m->m_next)
+			len += m->m_len;
+
+		if (len != 0)
+			break;
+		m_freem(sc->tx_buf_p);
+	}
+
+	if (len < SBNI_MIN_LEN)
+		len = SBNI_MIN_LEN;
+
+	sc->pktlen	= len;
+	sc->tx_frameno	= (len + sc->maxframe - 1) / sc->maxframe;
+	sc->framelen	= min(len, sc->maxframe);
+
+	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
+	sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+	if (sc->arpcom.ac_if.if_bpf)
+		bpf_mtap(&sc->arpcom.ac_if, sc->tx_buf_p);
+}
+
+
+static void
+drop_xmit_queue(struct sbni_softc *sc)
+{
+	struct mbuf *m;
+
+	if (sc->tx_buf_p) {
+		m_freem(sc->tx_buf_p);
+		sc->tx_buf_p = NULL;
+		sc->arpcom.ac_if.if_oerrors++;
+	}
+
+	for (;;) {
+		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
+		if (m == NULL)
+			break;
+		m_freem(m);
+		sc->arpcom.ac_if.if_oerrors++;
+	}
+
+	sc->tx_frameno	= 0;
+	sc->framelen	= 0;
+	sc->outpos	= 0;
+	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
+	sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+}
+
+
+static void
+send_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
+{
+	u_int32_t crc;
+	u_int len_field;
+	u_char value;
+
+	crc = *crc_p;
+	len_field = sc->framelen + 6;	/* CRC + frameno + reserved */
+
+	if (sc->state & FL_NEED_RESEND)
+		len_field |= FRAME_RETRY;	/* non-first attempt... */
+
+	if (sc->outpos == 0)
+		len_field |= FRAME_FIRST;
+
+	len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
+	sbni_outb(sc, DAT, SBNI_SIG);
+
+	value = (u_char)len_field;
+	sbni_outb(sc, DAT, value);
+	crc = CRC32(value, crc);
+	value = (u_char)(len_field >> 8);
+	sbni_outb(sc, DAT, value);
+	crc = CRC32(value, crc);
+
+	sbni_outb(sc, DAT, sc->tx_frameno);
+	crc = CRC32(sc->tx_frameno, crc);
+	sbni_outb(sc, DAT, 0);
+	crc = CRC32(0, crc);
+	*crc_p = crc;
+}
+
+
+/*
+ * if frame tail not needed (incorrect number or received twice),
+ * it won't store, but CRC will be calculated
+ */
+
+static int
+skip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
+{
+	while (tail_len--)
+		crc = CRC32(sbni_inb(sc, DAT), crc);
+
+	return (crc == CRC32_REMAINDER);
+}
+
+
+static int
+check_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
+	   u_int *ack, u_int *is_first, u_int32_t *crc_p)
+{
+	u_int32_t crc;
+	u_char value;
+
+	crc = *crc_p;
+	if (sbni_inb(sc, DAT) != SBNI_SIG)
+		return (0);
+
+	value = sbni_inb(sc, DAT);
+	*framelen = (u_int)value;
+	crc = CRC32(value, crc);
+	value = sbni_inb(sc, DAT);
+	*framelen |= ((u_int)value) << 8;
+	crc = CRC32(value, crc);
+
+	*ack = *framelen & FRAME_ACK_MASK;
+	*is_first = (*framelen & FRAME_FIRST) != 0;
+
+	if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
+		return (0);
+
+	value = sbni_inb(sc, DAT);
+	*frameno = (u_int)value;
+	crc = CRC32(value, crc);
+
+	crc = CRC32(sbni_inb(sc, DAT), crc);		/* reserved byte */
+	*framelen -= 2;
+
+	*crc_p = crc;
+	return (1);
+}
+
+
+static int
+get_rx_buf(struct sbni_softc *sc)
+{
+	struct mbuf *m;
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL) {
+		printf("sbni%d: cannot allocate header mbuf\n",
+		       sc->arpcom.ac_if.if_unit);
+		return (0);
+	}
+
+	/*
+	 * We always put the received packet in a single buffer -
+	 * either with just an mbuf header or in a cluster attached
+	 * to the header. The +2 is to compensate for the alignment
+	 * fixup below.
+	 */
+	if (ETHER_MAX_LEN + 2 > MHLEN) {
+		/* Attach an mbuf cluster */
+		MCLGET(m, M_DONTWAIT);
+		if ((m->m_flags & M_EXT) == 0) {
+			m_freem(m);
+			return (0);
+		}
+	}
+	m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
+
+	/*
+	 * The +2 is to longword align the start of the real packet.
+	 * (sizeof ether_header == 14)
+	 * This is important for NFS.
+	 */
+	m_adj(m, 2);
+	sc->rx_buf_p = m;
+	return (1);
+}
+
+
+static void
+indicate_pkt(struct sbni_softc *sc)
+{
+	struct mbuf *m;
+	struct ether_header *eh;
+
+	m = sc->rx_buf_p;
+	m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+	m->m_pkthdr.len   = m->m_len = sc->inppos;
+	eh = mtod(m, struct ether_header *);
+
+	/* Remove link layer address and indicate packet */
+	m_adj(m, sizeof(struct ether_header));
+	ether_input(&sc->arpcom.ac_if, eh, m);
+	sc->rx_buf_p = NULL;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Routine checks periodically wire activity and regenerates marker if
+ * connect was inactive for a long time.
+ */
+
+static void
+sbni_timeout(void *xsc)
+{
+	struct sbni_softc *sc;
+	int s;
+	u_char csr0;
+
+	sc = (struct sbni_softc *)xsc;
+	s = splimp();
+
+	csr0 = sbni_inb(sc, CSR0);
+	if (csr0 & RC_CHK) {
+
+		if (sc->timer_ticks) {
+			if (csr0 & (RC_RDY | BU_EMP))
+				/* receiving not active */
+				sc->timer_ticks--;
+		} else {
+			sc->in_stats.timeout_number++;
+			if (sc->delta_rxl)
+				timeout_change_level(sc);
+
+			sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
+			csr0 = sbni_inb(sc, CSR0);
+		}
+	}
+
+	sbni_outb(sc, CSR0, csr0 | RC_CHK); 
+	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
+	splx(s);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void
+card_start(struct sbni_softc *sc)
+{
+	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
+	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
+	sc->state |= FL_PREV_OK;
+
+	sc->inppos = 0;
+	sc->wait_frameno = 0;
+
+	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
+	sbni_outb(sc, CSR0, EN_INT);
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ *	generate an interrupt after a transmit has been started on it.
+ */
+
+static void
+sbni_watchdog(struct ifnet *ifp)
+{
+	log(LOG_ERR, "sbni%d: device timeout\n", ifp->if_unit);
+	ifp->if_oerrors++;
+}
+
+
+static u_char rxl_tab[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
+	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
+};
+
+#define SIZE_OF_TIMEOUT_RXL_TAB 4
+static u_char timeout_rxl_tab[] = {
+	0x03, 0x05, 0x08, 0x0b
+};
+
+static void
+set_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
+{
+	if (flags.fixed_rxl) {
+		sc->delta_rxl = 0; /* disable receive level autodetection */
+		sc->cur_rxl_index = flags.rxl;
+	} else {
+		sc->delta_rxl = DEF_RXL_DELTA;
+		sc->cur_rxl_index = DEF_RXL;
+	}
+   
+	sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
+	sc->csr1.rxl  = rxl_tab[sc->cur_rxl_index];
+	sc->maxframe  = DEFAULT_FRAME_LEN;
+   
+	/*
+	 * generate Ethernet address (0x00ff01xxxxxx)
+	 */
+	*(u_int16_t*)sc->arpcom.ac_enaddr = htons(0x00ff);
+	if (flags.mac_addr)
+		*(u_int32_t*)(sc->arpcom.ac_enaddr+2) =
+			htonl(flags.mac_addr | 0x01000000);
+	else {
+		/* reading timer value */
+		outb(0x43, 0);
+		insb(0x40, sc->arpcom.ac_enaddr + 3, 4);
+		*(u_char*)(sc->arpcom.ac_enaddr + 2) = 0x01;
+	}
+}
+
+
+#ifdef SBNI_DUAL_COMPOUND
+
+#ifndef offsetof
+#define offsetof(type, member)		((u_int32_t)(&((type *)0)->member))
+#endif
+
+
+struct sbni_softc *
+connect_to_master(struct sbni_softc *sc)
+{
+	struct sbni_softc *p;
+
+	p = (struct sbni_softc *)(((char *)&headlist)
+	    - offsetof(struct sbni_softc, link));
+
+	for (; p->link; p = p->link) {
+		if (p->link->irq == sc->irq
+		    && (p->link->base_addr == sc->base_addr + 4
+			|| p->link->base_addr == sc->base_addr - 4)) {
+
+			struct sbni_softc  *t = p->link;
+			t->slave_sc = sc;
+			p->link = p->link->link;
+			return (t);
+		}
+	}
+
+	return (NULL);
+}
+
+#endif	/* SBNI_DUAL_COMPOUND */
+
+
+/* Receive level auto-selection */
+
+static void
+change_level(struct sbni_softc *sc)
+{
+	if (sc->delta_rxl == 0)		/* do not auto-negotiate RxL */
+		return;
+
+	if (sc->cur_rxl_index == 0)
+		sc->delta_rxl = 1;
+	else if (sc->cur_rxl_index == 15)
+		sc->delta_rxl = -1;
+	else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
+		sc->delta_rxl = -sc->delta_rxl;
+
+	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
+	sbni_inb(sc, CSR0);	/* it needed for PCI cards */
+	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
+
+	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
+	sc->cur_rxl_rcvd  = 0;
+}
+
+
+static void
+timeout_change_level(struct sbni_softc *sc)
+{
+	sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
+	if (++sc->timeout_rxl >= 4)
+		sc->timeout_rxl = 0;
+
+	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
+	sbni_inb(sc, CSR0);
+	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
+
+	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
+	sc->cur_rxl_rcvd  = 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Process an ioctl request. This code needs some work - it looks
+ *	pretty ugly.
+ */
+
+static int
+sbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+	struct sbni_softc *sc;
+	struct ifreq *ifr;
+	struct proc *p;
+	struct sbni_in_stats *in_stats;
+	struct sbni_flags flags;
+	int error, s;
+
+	sc = ifp->if_softc;
+	ifr = (struct ifreq *)data;
+	p = curproc;
+	error = 0;
+
+	s = splimp();
+
+	switch (command) {
+	case SIOCSIFADDR:
+	case SIOCGIFADDR:
+			ether_ioctl(ifp, command, data);
+			break;
+
+	case SIOCSIFFLAGS:
+		/*
+		 * If the interface is marked up and stopped, then start it.
+		 * If it is marked down and running, then stop it.
+		 */
+		if (ifp->if_flags & IFF_UP) {
+			if (!(ifp->if_flags & IFF_RUNNING))
+				sbni_init(sc);
+		} else {
+			if (ifp->if_flags & IFF_RUNNING) {
+				sbni_stop(sc);
+				ifp->if_flags &= ~IFF_RUNNING;
+			}
+		}
+		break;
+
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		/*
+		 * Multicast list has changed; set the hardware filter
+		 * accordingly.
+		 */
+		error = 0;
+		/* if (ifr == NULL)
+			error = EAFNOSUPPORT; */
+		break;
+
+	case SIOCSIFMTU:
+		if (ifr->ifr_mtu > ETHERMTU)
+			error = EINVAL;
+		else
+			ifp->if_mtu = ifr->ifr_mtu;
+		break;
+
+		/*
+		 * SBNI specific ioctl
+		 */
+	case SIOCGHWFLAGS:	/* get flags */
+		bcopy((caddr_t) sc->arpcom.ac_enaddr+3, (caddr_t) &flags, 3);
+		flags.rxl = sc->cur_rxl_index;
+		flags.rate = sc->csr1.rate;
+		flags.fixed_rxl = (sc->delta_rxl == 0);
+		flags.fixed_rate = 1;
+		ifr->ifr_data = *(caddr_t*) &flags;
+		break;
+
+	case SIOCGINSTATS:
+		in_stats = (struct sbni_in_stats *)ifr->ifr_data;
+		bcopy((void *)(&(sc->in_stats)), (void *)in_stats,
+		      sizeof(struct sbni_in_stats));
+		break;
+
+	case SIOCSHWFLAGS:	/* set flags */
+		/* root only */
+		error = suser(p);
+		if (error)
+			break;
+		flags = *(struct sbni_flags*)&ifr->ifr_data;
+		if (flags.fixed_rxl) {
+			sc->delta_rxl = 0;
+			sc->cur_rxl_index = flags.rxl;
+		} else {
+			sc->delta_rxl = DEF_RXL_DELTA;
+			sc->cur_rxl_index = DEF_RXL;
+		}
+		sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
+		sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
+		if (flags.mac_addr)
+			bcopy((caddr_t) &flags,
+			      (caddr_t) sc->arpcom.ac_enaddr+3, 3);
+
+		/* Don't be afraid... */
+		sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
+		break;
+
+	case SIOCRINSTATS:
+		if (!(error = suser(p)))	/* root only */
+			bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
+		break;
+
+	default:
+		error = EINVAL;
+	}
+
+	splx(s);
+	return (error);
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef ASM_CRC
+
+static u_int32_t
+calc_crc32(u_int32_t crc, caddr_t p, u_int len)
+{
+	register u_int32_t  _crc __asm ("ax");
+	_crc = crc;
+	
+	__asm __volatile (
+		"xorl	%%ebx, %%ebx\n"
+		"movl	%1, %%esi\n" 
+		"movl	%2, %%ecx\n" 
+		"movl	$crc32tab, %%edi\n"
+		"shrl	$2, %%ecx\n"
+		"jz	1f\n"
+
+		".align 4\n"
+	"0:\n"
+		"movb	%%al, %%bl\n"
+		"movl	(%%esi), %%edx\n"
+		"shrl	$8, %%eax\n"
+		"xorb	%%dl, %%bl\n"
+		"shrl	$8, %%edx\n"
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+
+		"movb	%%al, %%bl\n"
+		"shrl	$8, %%eax\n"
+		"xorb	%%dl, %%bl\n"
+		"shrl	$8, %%edx\n"
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+
+		"movb	%%al, %%bl\n"
+		"shrl	$8, %%eax\n"
+		"xorb	%%dl, %%bl\n"
+		"movb	%%dh, %%dl\n" 
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+
+		"movb	%%al, %%bl\n"
+		"shrl	$8, %%eax\n"
+		"xorb	%%dl, %%bl\n"
+		"addl	$4, %%esi\n"
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+
+		"decl	%%ecx\n"
+		"jnz	0b\n"
+
+	"1:\n"
+		"movl	%2, %%ecx\n"
+		"andl	$3, %%ecx\n"
+		"jz	2f\n"
+
+		"movb	%%al, %%bl\n"
+		"shrl	$8, %%eax\n"
+		"xorb	(%%esi), %%bl\n"
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+
+		"decl	%%ecx\n"
+		"jz	2f\n"
+
+		"movb	%%al, %%bl\n"
+		"shrl	$8, %%eax\n"
+		"xorb	1(%%esi), %%bl\n"
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+
+		"decl	%%ecx\n"
+		"jz	2f\n"
+
+		"movb	%%al, %%bl\n"
+		"shrl	$8, %%eax\n"
+		"xorb	2(%%esi), %%bl\n"
+		"xorl	(%%edi,%%ebx,4), %%eax\n"
+	"2:\n"
+		:
+		: "a" (_crc), "g" (p), "g" (len)
+		: "ax", "bx", "cx", "dx", "si", "di"
+	);
+
+	return (_crc);
+}
+
+#else	/* ASM_CRC */
+
+static u_int32_t
+calc_crc32(u_int32_t crc, caddr_t p, u_int len)
+{
+	while (len--)
+		crc = CRC32(*p++, crc);
+
+	return (crc);
+}
+
+#endif	/* ASM_CRC */
+
+
+static u_int32_t crc32tab[] __attribute__ ((aligned(8))) = {
+	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
+	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
+	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
+	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
+	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
+	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
+	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
+	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
+	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
+	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
+	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
+	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
+	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
+	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
+	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
+	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
+	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
+	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
+	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
+	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
+	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
+	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
+	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
+	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
+	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
+	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
+	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
+	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
+	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
+	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
+	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
+	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
+	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
+	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
+	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
+	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
+	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
+	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
+	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
+	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
+	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
+	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
+	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
+	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
+	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
+	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
+	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
+	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
+	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
+	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
+	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
+	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
+	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
+	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
+	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
+	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
+	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
+	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
+	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
+	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
+	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
+	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
+	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
+	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
+};
diff --git a/sys/dev/sbni/if_sbni_isa.c b/sys/dev/sbni/if_sbni_isa.c
new file mode 100644
index 000000000000..a4aefbcf9709
--- /dev/null
+++ b/sys/dev/sbni/if_sbni_isa.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
+ * Author: Denis I.Timofeev <timofeev@granch.ru>
+ *
+ * Redistributon and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+
+#include <sys/bus.h>
+#include <sys/bus_private.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include <isa/isavar.h>
+
+#include <dev/sbni/if_sbnireg.h>
+#include <dev/sbni/if_sbnivar.h>
+
+static int	sbni_probe_isa(device_t);
+static int	sbni_attach_isa(device_t);
+
+static device_method_t sbni_isa_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,	sbni_probe_isa),
+	DEVMETHOD(device_attach, sbni_attach_isa),
+	{ 0, 0 }
+};
+
+static driver_t sbni_isa_driver = {
+	"sbni",
+	sbni_isa_methods,
+	sizeof(struct sbni_softc)
+};
+
+static devclass_t sbni_isa_devclass;
+static struct isa_pnp_id  sbni_ids[] = {
+	{ 0, NULL }	/* we have no pnp sbni cards atm.  */
+};
+
+DRIVER_MODULE(sbni, isa, sbni_isa_driver, sbni_isa_devclass, 0, 0);
+
+
+static int
+sbni_probe_isa(device_t dev)
+{
+	struct sbni_softc *sc;
+	int error;
+
+	error = ISA_PNP_PROBE(device_get_parent(dev), dev, sbni_ids);
+	if (error && error != ENOENT)
+		return (error);
+
+	sc = device_get_softc(dev);
+	bzero(sc, sizeof(struct sbni_softc));
+
+ 	sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->io_rid,
+					0ul, ~0ul, SBNI_PORTS, RF_ACTIVE);
+	if (!sc->io_res) {
+		printf("sbni: cannot allocate io ports!\n");
+		return (ENOENT);
+	}
+
+	sc->base_addr = rman_get_start(sc->io_res);
+	if (sbni_probe(sc) != 0) {
+		bus_release_resource(dev, SYS_RES_IOPORT,
+				     sc->io_rid, sc->io_res);
+		return (ENXIO);
+	}
+
+	device_quiet(dev);
+	return (0);
+}
+
+
+static int
+sbni_attach_isa(device_t dev)
+{
+	struct sbni_softc *sc;
+	struct sbni_flags flags;
+	int error;
+   
+	sc = device_get_softc(dev);
+
+	printf("sbni%d: <Granch SBNI12/ISA adapter> port 0x%x",
+	       next_sbni_unit, sc->base_addr);
+	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
+					 0ul, ~0ul, 1, RF_ACTIVE);
+
+	if (sc->irq_res) {
+		printf(" irq %ld\n", rman_get_start(sc->irq_res));
+		error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
+				       sbni_intr, sc, &sc->irq_handle);
+		if (error) {
+			printf("sbni%d: bus_setup_intr\n", next_sbni_unit);
+			bus_release_resource(dev, SYS_RES_IOPORT,
+					     sc->io_rid, sc->io_res);
+			return (error);
+		}
+
+#ifndef SBNI_DUAL_COMPOUND
+
+	} else {
+		printf("\nsbni%d: irq conflict!\n", next_sbni_unit);
+		bus_release_resource(dev, SYS_RES_IOPORT,
+				     sc->io_rid, sc->io_res);
+		return (ENOENT);
+	}
+
+#else	/* SBNI_DUAL_COMPOUND */
+
+		sc->link = headlist;
+		headlist = sc;
+	} else {
+		struct sbni_softc  *master;
+
+		if ((master = connect_to_master(sc)) == 0) {
+			printf("\nsbni%d: failed to alloc irq\n",
+			       next_sbni_unit);
+			bus_release_resource(dev, SYS_RES_IOPORT,
+					     sc->io_rid, sc->io_res);
+			return (ENXIO);
+		} else
+			printf(" shared irq with sbni%d\n",
+			       master->arpcom.ac_if.if_unit);
+	} 
+#endif	/* SBNI_DUAL_COMPOUND */
+
+	*(u_int32_t*)&flags = device_get_flags(dev);
+
+	sbni_attach(sc, next_sbni_unit++, flags);
+	return (0);
+}
diff --git a/sys/dev/sbni/if_sbni_pci.c b/sys/dev/sbni/if_sbni_pci.c
new file mode 100644
index 000000000000..658e943545c7
--- /dev/null
+++ b/sys/dev/sbni/if_sbni_pci.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
+ * Author: Denis I.Timofeev <timofeev@granch.ru>
+ *
+ * Redistributon and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+ 
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/bus.h>
+#include <sys/bus_private.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+
+#include <dev/sbni/if_sbnireg.h>
+#include <dev/sbni/if_sbnivar.h>
+
+static int	sbni_pci_probe(device_t);
+static int	sbni_pci_attach(device_t);
+
+static device_method_t sbni_pci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,	sbni_pci_probe),
+	DEVMETHOD(device_attach, sbni_pci_attach),
+	{ 0, 0 }
+};
+
+static driver_t sbni_pci_driver = {
+	"sbni",
+	sbni_pci_methods,
+	sizeof(struct sbni_softc)
+};
+
+static devclass_t sbni_pci_devclass;
+
+DRIVER_MODULE(sbni, pci, sbni_pci_driver, sbni_pci_devclass, 0, 0);
+
+
+static int
+sbni_pci_probe(device_t dev)
+{
+	struct sbni_softc  *sc;
+	u_int32_t  ports;
+   
+	ports = SBNI_PORTS;
+	if (pci_get_vendor(dev) != SBNI_PCI_VENDOR
+	    || pci_get_device(dev) != SBNI_PCI_DEVICE)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	bzero(sc, sizeof(struct sbni_softc));
+	if (pci_get_subdevice(dev) == 2) {
+		ports <<= 1;
+		sc->slave_sc = malloc(sizeof(struct sbni_softc),
+				      M_DEVBUF, M_NOWAIT);
+		if (!sc->slave_sc)
+			return (ENOMEM);
+		bzero(sc->slave_sc, sizeof(struct sbni_softc));
+		device_set_desc(dev, "Granch SBNI12/PCI Dual adapter");
+	} else
+		device_set_desc(dev, "Granch SBNI12/PCI adapter");
+
+	sc->io_rid = PCIR_MAPS;
+ 	sc->io_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->io_rid,
+					0ul, ~0ul, ports, RF_ACTIVE);
+	if (!sc->io_res) {
+		printf("sbni: cannot allocate io ports!\n");
+		if (sc->slave_sc)
+			free(sc->slave_sc, M_DEVBUF);
+		return (ENOENT);
+	}
+
+	sc->base_addr = rman_get_start(sc->io_res);
+	if (sc->slave_sc)
+		sc->slave_sc->base_addr = sc->base_addr + 4;
+	if (sbni_probe(sc) != 0) {
+		bus_release_resource(dev, SYS_RES_IOPORT,
+				     sc->io_rid, sc->io_res);
+		if (sc->slave_sc)
+			free(sc->slave_sc, M_DEVBUF);
+		return (ENXIO);
+	}
+
+	device_quiet(dev);
+	return (0);
+}
+
+static int
+sbni_pci_attach(device_t dev)
+{
+	struct sbni_softc *sc;
+	struct sbni_flags flags;
+	int error;
+
+	sc = device_get_softc(dev);
+
+	printf("sbni%d: <Granch SBNI12/PCI%sadapter> port 0x%x",
+	       next_sbni_unit, sc->slave_sc ? " Dual " : " ", sc->base_addr);
+	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
+					 0ul, ~0ul, 1, RF_SHAREABLE);
+
+	if (sc->irq_res) {
+		printf(" irq %ld\n", rman_get_start(sc->irq_res));
+		error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
+				       sbni_intr, sc, &sc->irq_handle);
+		if (error) {
+			printf("sbni%d: bus_setup_intr\n", next_sbni_unit);
+			goto attach_failed;
+		}
+	} else {
+		printf("\nsbni%d: cannot claim irq!\n", next_sbni_unit);
+		error = ENOENT;
+		goto attach_failed;
+	}
+
+	*(u_int32_t*)&flags = 0;
+
+	sbni_attach(sc, next_sbni_unit++, flags);
+	if (sc->slave_sc)
+		sbni_attach(sc->slave_sc, next_sbni_unit++, flags);
+	return (0);
+
+attach_failed:
+	bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid, sc->io_res);
+	if (sc->slave_sc)
+		free(sc->slave_sc, M_DEVBUF);
+	return (error);
+}
diff --git a/sys/dev/sbni/if_sbnireg.h b/sys/dev/sbni/if_sbnireg.h
new file mode 100644
index 000000000000..70fca443051b
--- /dev/null
+++ b/sys/dev/sbni/if_sbnireg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
+ * Author: Denis I.Timofeev <timofeev@granch.ru>
+ *
+ * Redistributon and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * We don't have registered vendor id yet...
+ */
+#define SBNI_PCI_VENDOR 	0x55 
+#define SBNI_PCI_DEVICE 	0x9f
+
+#define ISA_MODE 0x00
+#define PCI_MODE 0x01
+
+#define	SBNI_PORTS	4
+
+enum sbni_reg {
+	CSR0 = 0,
+	CSR1 = 1,
+	DAT  = 2
+};
+
+/* CSR0 mapping */
+enum {
+	BU_EMP = 0x02,
+	RC_CHK = 0x04,
+	CT_ZER = 0x08,
+	TR_REQ = 0x10,
+	TR_RDY = 0x20,
+	EN_INT = 0x40,
+	RC_RDY = 0x80
+};
+
+
+/* CSR1 mapping */
+#define PR_RES 0x80
+
+struct sbni_csr1 {
+	unsigned rxl	: 5;
+	unsigned rate	: 2;
+	unsigned 	: 1;
+};
+
+
+
+#define FRAME_ACK_MASK  (u_int16_t)0x7000
+#define FRAME_LEN_MASK  (u_int16_t)0x03FF
+#define FRAME_FIRST     (u_int16_t)0x8000
+#define FRAME_RETRY     (u_int16_t)0x0800
+
+#define FRAME_SENT_BAD  (u_int16_t)0x4000
+#define FRAME_SENT_OK   (u_int16_t)0x3000
+
+
+enum {
+	FL_WAIT_ACK    = 1,
+	FL_NEED_RESEND = 2,
+	FL_PREV_OK     = 4,
+	FL_SLOW_MODE   = 8
+};
+
+
+enum {
+	DEFAULT_IOBASEADDR = 0x210,
+	DEFAULT_INTERRUPTNUMBER = 5,
+	DEFAULT_RATE = 0,
+	DEFAULT_FRAME_LEN = 1012
+};
+
+#define DEF_RXL_DELTA	-1
+#define DEF_RXL		0xf
+
+#define SBNI_SIG 0x5a
+
+#define	SBNI_MIN_LEN	(ETHER_MIN_LEN - 4)
+#define SBNI_MAX_FRAME	1023
+
+#define SBNI_HZ 18 /* ticks to wait for pong or packet */
+		/* sbni watchdog called SBNI_HZ times per sec. */
+
+#define TR_ERROR_COUNT 32
+#define CHANGE_LEVEL_START_TICKS 4
diff --git a/sys/dev/sbni/if_sbnivar.h b/sys/dev/sbni/if_sbnivar.h
new file mode 100644
index 000000000000..445325156c3e
--- /dev/null
+++ b/sys/dev/sbni/if_sbnivar.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
+ * Author: Denis I.Timofeev <timofeev@granch.ru>
+ *
+ * Redistributon and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice unmodified, this list of conditions, and the following
+ *    disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * SBNI12 definitions
+ */
+
+/*
+ * CONFIGURATION PARAMETER:
+ *
+ *	Uncomment this if you want to use model SBNI12D-11/ISA with same IRQ
+ *	for both first and second channels.
+ */
+#define SBNI_DUAL_COMPOUND 1
+
+#define SBNI_DEBUG 0
+
+#if SBNI_DEBUG
+#define DP(A) A
+#else
+#define DP(A)
+#endif
+
+struct sbni_in_stats {
+	u_int32_t	all_rx_number;
+	u_int32_t	bad_rx_number;
+	u_int32_t	timeout_number;
+	u_int32_t	all_tx_number;
+	u_int32_t	resend_tx_number;
+};
+
+struct sbni_flags {
+	u_int	mac_addr	: 24;
+	u_int	rxl		: 4;
+	u_int	rate		: 2;
+	u_int	fixed_rxl	: 1;
+	u_int	fixed_rate	: 1;
+};
+
+
+#ifdef _KERNEL	/* to avoid compile this decls with sbniconfig */
+
+struct sbni_softc {
+	struct	arpcom arpcom;		/* ethernet common */
+
+	int	base_addr;
+	int	irq;
+	int	io_rid;
+	int	irq_rid;
+	struct	resource *io_res;
+	struct	resource *irq_res;
+	void	*irq_handle;
+
+	struct	mbuf *rx_buf_p;		/* receive buffer ptr */
+	struct	mbuf *tx_buf_p;		/* transmit buffer ptr */
+	
+	u_int	pktlen;			/* length of transmitting pkt */
+	u_int	framelen;		/* current frame length */
+	u_int	maxframe;		/* maximum valid frame length */
+	u_int	state;
+	u_int	inppos;			/* positions in rx/tx buffers */
+	u_int	outpos;			/* positions in rx/tx buffers */
+
+	/* transmitting frame number - from frames qty to 1 */
+	u_int	tx_frameno;
+
+	/* expected number of next receiving frame */
+	u_int	wait_frameno;
+
+	/* count of failed attempts to frame send - 32 attempts do before
+	   error - while receiver tunes on opposite side of wire */
+	u_int	trans_errors;
+
+	/* idle time; send pong when limit exceeded */
+	u_int	timer_ticks;
+
+	/* fields used for receive level autoselection */
+	int	delta_rxl;
+	u_int	cur_rxl_index;
+	u_int	timeout_rxl;
+	u_int32_t	cur_rxl_rcvd;
+	u_int32_t	prev_rxl_rcvd;
+
+	struct	sbni_csr1 csr1;			/* current value of CSR1 */
+	struct	sbni_in_stats in_stats; 	/* internal statistics */ 
+
+	struct	callout_handle wch;
+
+	struct	sbni_softc *slave_sc;
+
+#ifdef SBNI_DUAL_COMPOUND
+	struct	sbni_softc *link;
+#endif
+};
+
+void	sbni_intr(void *);
+int	sbni_probe(struct sbni_softc *);
+void	sbni_attach(struct sbni_softc *, int, struct sbni_flags);
+
+extern u_int32_t next_sbni_unit;
+
+#ifdef SBNI_DUAL_COMPOUND
+extern struct sbni_softc *headlist;
+
+struct sbni_softc	*connect_to_master(struct sbni_softc *);
+#endif
+#endif	/* _KERNEL */
+
+/*
+ * SBNI socket ioctl params
+ */
+#define	SIOCGHWFLAGS	_IOWR('i', 62, struct ifreq)	/* get flags */
+#define	SIOCSHWFLAGS	_IOWR('i', 61, struct ifreq)	/* set flags */
+#define SIOCGINSTATS	_IOWR('i', 60, struct ifreq)	/* get internal stats */
+#define SIOCRINSTATS	_IOWR('i', 63, struct ifreq)	/* reset internal stats */
+
+
+/*
+ * CRC-32 stuff
+ */
+#define CRC32(c,crc) (crc32tab[((size_t)(crc) ^ (c)) & 0xff] ^ (((crc) >> 8) & 0x00ffffff))
+      /* CRC generator EDB88320 */
+      /* CRC remainder 2144DF1C */
+      /* CRC initial value 0 */
+#define CRC32_REMAINDER 0x2144df1c
+#define CRC32_INITIAL 0x00000000
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index e56a06e87652..3a436c08b5aa 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -1723,6 +1723,7 @@ device		miibus
 #       Olicom PCI token-ring adapters OC-3136, OC-3137, OC-3139, OC-3140,
 #       OC-3141, OC-3540, OC-3250
 # rdp:  RealTek RTL 8002-based pocket ethernet adapters
+# sbni:	Granch SBNI12-xx ISA and PCI adapters
 # pcn:	Support for PCI fast ethernet adapters based on the AMD Am79c97x
 #	chipsets, including the PCnet/FAST, PCnet/FAST+, PCnet/PRO and
 #	PCnet/Home. These were previously handled by the lnc driver (and
@@ -1841,6 +1842,11 @@ hint.rdp.0.at="isa"
 hint.rdp.0.port="0x378"
 hint.rdp.0.irq="7"
 hint.rdp.0.flags="2"
+device		sbni	1
+hint.sbni.0.at="isa"
+hint.sbni.0.port="0x210"
+hint.sbni.0.irq="0xefdead"
+hint.sbni.0.flags="0"
 device		sr	1
 hint.sr.0.at="isa"
 hint.sr.0.port="0x300"
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index fdb5ed87e4e5..184e3c7538c0 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -133,6 +133,7 @@ SUBDIR+=aac \
 	pecoff \
 	ray \
 	s3 \
+	sbni \
 	splash \
 	sr \
 	streams \
diff --git a/sys/modules/sbni/Makefile b/sys/modules/sbni/Makefile
new file mode 100644
index 000000000000..9dfab90f59ab
--- /dev/null
+++ b/sys/modules/sbni/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/sbni
+
+KMOD=	if_sbni
+SRCS=	if_sbni.c if_sbni_isa.c if_sbni_pci.c
+
+SRCS+=	bus_if.h device_if.h isa_if.h pci_if.h
+
+.include <bsd.kmod.mk>