if_sbni.c revision 121816
12088Ssos/*
25536Ssos * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
32088Ssos * Author: Denis I.Timofeev <timofeev@granch.ru>
42088Ssos *
52088Ssos * Redistributon and use in source and binary forms, with or without
62088Ssos * modification, are permitted provided that the following conditions
72088Ssos * are met:
82088Ssos * 1. Redistributions of source code must retain the above copyright
95994Ssos *    notice unmodified, this list of conditions, and the following
105994Ssos *    disclaimer.
112088Ssos * 2. Redistributions in binary form must reproduce the above copyright
122088Ssos *    notice, this list of conditions and the following disclaimer in the
132088Ssos *    documentation and/or other materials provided with the distribution.
142088Ssos *
152088Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
162088Ssos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
172088Ssos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182088Ssos * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192088Ssos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
202088Ssos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
212088Ssos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
222088Ssos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
232088Ssos * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
242088Ssos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
252088Ssos * SUCH DAMAGE.
262088Ssos *
272088Ssos */
282088Ssos
2929603Scharnier#include <sys/cdefs.h>
3029603Scharnier__FBSDID("$FreeBSD: head/sys/dev/sbni/if_sbni.c 121816 2003-10-31 18:32:15Z brooks $");
3138139Syokota
3229603Scharnier/*
3329603Scharnier * Device driver for Granch SBNI12 leased line adapters
342088Ssos *
3529603Scharnier * Revision 2.0.0  1997/08/06
362088Ssos * Initial revision by Alexey Zverev
3729603Scharnier *
383864Sswallace * Revision 2.0.1 1997/08/11
3929603Scharnier * Additional internal statistics support (tx statistics)
402088Ssos *
412088Ssos * Revision 2.0.2 1997/11/05
422088Ssos * if_bpf bug has been fixed
432088Ssos *
442088Ssos * Revision 2.0.3 1998/12/20
458857Srgrimes * Memory leakage has been eliminated in
462088Ssos * the sbni_st and sbni_timeout routines.
472088Ssos *
4838139Syokota * Revision 3.0 2000/08/10 by Yaroslav Polyakov
492088Ssos * Support for PCI cards. 4.1 modification.
502088Ssos *
5132316Syokota * Revision 3.1 2000/09/12
5232316Syokota * Removed extra #defines around bpf functions
5332316Syokota *
5432316Syokota * Revision 4.0 2000/11/23 by Denis Timofeev
5532316Syokota * Completely redesigned the buffer management
5632316Syokota *
5732316Syokota * Revision 4.1 2001/01/21
5832316Syokota * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
5932316Syokota *
6032316Syokota * Written with reference to NE2000 driver developed by David Greenman.
6132316Syokota */
6232316Syokota
635994Ssos
645994Ssos#include <sys/param.h>
655994Ssos#include <sys/systm.h>
665994Ssos#include <sys/socket.h>
675994Ssos#include <sys/sockio.h>
685994Ssos#include <sys/mbuf.h>
695994Ssos#include <sys/kernel.h>
705994Ssos#include <sys/proc.h>
715994Ssos#include <sys/callout.h>
725994Ssos#include <sys/syslog.h>
735994Ssos#include <sys/random.h>
745994Ssos
759202Srgrimes#include <machine/bus.h>
765994Ssos#include <sys/rman.h>
775994Ssos#include <machine/resource.h>
785994Ssos
799202Srgrimes#include <net/if.h>
805994Ssos#include <net/ethernet.h>
815994Ssos#include <net/if_arp.h>
825994Ssos#include <net/bpf.h>
835994Ssos
845994Ssos#include <dev/sbni/if_sbnireg.h>
855994Ssos#include <dev/sbni/if_sbnivar.h>
865994Ssos
875994Ssos#define ASM_CRC 1
882088Ssos
892088Ssosstatic void	sbni_init(void *);
902088Ssosstatic void	sbni_start(struct ifnet *);
912088Ssosstatic int	sbni_ioctl(struct ifnet *, u_long, caddr_t);
922088Ssosstatic void	sbni_watchdog(struct ifnet *);
932088Ssosstatic void	sbni_stop(struct sbni_softc *);
942088Ssosstatic void	handle_channel(struct sbni_softc *);
952088Ssos
962088Ssosstatic void	card_start(struct sbni_softc *);
972088Ssosstatic int	recv_frame(struct sbni_softc *);
986046Ssosstatic void	send_frame(struct sbni_softc *);
992088Ssosstatic int	upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
10032316Syokotastatic int	skip_tail(struct sbni_softc *, u_int, u_int32_t);
1012088Ssosstatic void	interpret_ack(struct sbni_softc *, u_int);
10229603Scharnierstatic void	download_data(struct sbni_softc *, u_int32_t *);
1032088Ssosstatic void	prepare_to_send(struct sbni_softc *);
1042088Ssosstatic void	drop_xmit_queue(struct sbni_softc *);
1052088Ssosstatic int	get_rx_buf(struct sbni_softc *);
1062088Ssosstatic void	indicate_pkt(struct sbni_softc *);
1072088Ssosstatic void	change_level(struct sbni_softc *);
1082088Ssosstatic int	check_fhdr(struct sbni_softc *, u_int *, u_int *,
10929603Scharnier			   u_int *, u_int *, u_int32_t *);
1102088Ssosstatic int	append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
1112088Ssosstatic void	timeout_change_level(struct sbni_softc *);
1122088Ssosstatic void	send_frame_header(struct sbni_softc *, u_int32_t *);
1132088Ssosstatic void	set_initial_values(struct sbni_softc *, struct sbni_flags);
1142088Ssos
1152088Ssosstatic u_int32_t	calc_crc32(u_int32_t, caddr_t, u_int);
1162088Ssosstatic timeout_t	sbni_timeout;
1172088Ssos
1185536Ssosstatic __inline u_char	sbni_inb(struct sbni_softc *, enum sbni_reg);
1195536Ssosstatic __inline void	sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
1205536Ssosstatic __inline void	sbni_insb(struct sbni_softc *, u_char *, u_int);
1212088Ssosstatic __inline void	sbni_outsb(struct sbni_softc *, u_char *, u_int);
1222088Ssos
1232088Ssosstatic u_int32_t crc32tab[];
1242088Ssos
1252088Ssos#ifdef SBNI_DUAL_COMPOUND
1262088Ssosstruct sbni_softc *sbni_headlist;
1272088Ssos#endif
1282088Ssos
1292088Ssosu_int32_t next_sbni_unit;
1302088Ssos
1312088Ssos/* -------------------------------------------------------------------------- */
1322088Ssos
1332088Ssosstatic __inline u_char
1342088Ssossbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
1352088Ssos{
1362088Ssos	return bus_space_read_1(
1372088Ssos	    rman_get_bustag(sc->io_res),
1382088Ssos	    rman_get_bushandle(sc->io_res),
1392088Ssos	    sc->io_off + reg);
1402088Ssos}
1412088Ssos
1422088Ssosstatic __inline void
1432088Ssossbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
14432316Syokota{
1452088Ssos	bus_space_write_1(
1462088Ssos	    rman_get_bustag(sc->io_res),
1472088Ssos	    rman_get_bushandle(sc->io_res),
1482088Ssos	    sc->io_off + reg, value);
1492088Ssos}
1502088Ssos
1512088Ssosstatic __inline void
1522088Ssossbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
1532088Ssos{
1542088Ssos	bus_space_read_multi_1(
1552088Ssos	    rman_get_bustag(sc->io_res),
1562088Ssos	    rman_get_bushandle(sc->io_res),
1572088Ssos	    sc->io_off + DAT, to, len);
1582088Ssos}
1592088Ssos
1602088Ssosstatic __inline void
1612088Ssossbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
1622088Ssos{
1632088Ssos	bus_space_write_multi_1(
1642088Ssos	    rman_get_bustag(sc->io_res),
1652088Ssos	    rman_get_bushandle(sc->io_res),
1662088Ssos	    sc->io_off + DAT, from, len);
1672088Ssos}
1682088Ssos
1692088Ssos
1702088Ssos/*
1712088Ssos	Valid combinations in CSR0 (for probing):
1722088Ssos
1732088Ssos	VALID_DECODER	0000,0011,1011,1010
1742088Ssos
1752088Ssos				    	; 0   ; -
1762088Ssos				TR_REQ	; 1   ; +
1772088Ssos			TR_RDY	    	; 2   ; -
1782088Ssos			TR_RDY	TR_REQ	; 3   ; +
1795994Ssos		BU_EMP		    	; 4   ; +
1805994Ssos		BU_EMP	     	TR_REQ	; 5   ; +
18138053Syokota		BU_EMP	TR_RDY	    	; 6   ; -
18238053Syokota		BU_EMP	TR_RDY	TR_REQ	; 7   ; +
18332316Syokota	RC_RDY 		     		; 8   ; +
18432316Syokota	RC_RDY			TR_REQ	; 9   ; +
18532316Syokota	RC_RDY		TR_RDY		; 10  ; -
18632316Syokota	RC_RDY		TR_RDY	TR_REQ	; 11  ; -
1872088Ssos	RC_RDY	BU_EMP			; 12  ; -
1882088Ssos	RC_RDY	BU_EMP		TR_REQ	; 13  ; -
1892088Ssos	RC_RDY	BU_EMP	TR_RDY		; 14  ; -
1902088Ssos	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; -
1912088Ssos*/
1922088Ssos
1932088Ssos#define VALID_DECODER	(2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
1942088Ssos
1952088Ssos
1962088Ssosint
1972088Ssossbni_probe(struct sbni_softc *sc)
1982088Ssos{
1992088Ssos	u_char csr0;
2002088Ssos
2012088Ssos	csr0 = sbni_inb(sc, CSR0);
2022088Ssos	if (csr0 != 0xff && csr0 != 0x00) {
2032088Ssos		csr0 &= ~EN_INT;
2042088Ssos		if (csr0 & BU_EMP)
2052088Ssos			csr0 |= EN_INT;
2062088Ssos
20732316Syokota		if (VALID_DECODER & (1 << (csr0 >> 4)))
2082088Ssos			return (0);
20932316Syokota	}
2102088Ssos
2112088Ssos	return (ENXIO);
2122088Ssos}
21332316Syokota
21432316Syokota
21532316Syokota/*
21632316Syokota * Install interface into kernel networking data structures
21732316Syokota */
21832316Syokotavoid
21932316Syokotasbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
22032316Syokota{
22132316Syokota	struct ifnet *ifp;
22232316Syokota	u_char csr0;
22332316Syokota
22432316Syokota	ifp = &sc->arpcom.ac_if;
22532316Syokota	sbni_outb(sc, CSR0, 0);
22632316Syokota	set_initial_values(sc, flags);
22732316Syokota
22832316Syokota	callout_handle_init(&sc->wch);
22932316Syokota	/* Initialize ifnet structure */
23032316Syokota	ifp->if_softc	= sc;
23132316Syokota	if_initname(ifp, "sbni", unit);
2322088Ssos	ifp->if_init	= sbni_init;
23332316Syokota	ifp->if_start	= sbni_start;
23432316Syokota	ifp->if_output	= ether_output;
23532316Syokota	ifp->if_ioctl	= sbni_ioctl;
23632316Syokota	ifp->if_watchdog	= sbni_watchdog;
23732316Syokota	ifp->if_snd.ifq_maxlen	= IFQ_MAXLEN;
23832316Syokota
23932316Syokota	/* report real baud rate */
24032316Syokota	csr0 = sbni_inb(sc, CSR0);
24132316Syokota	ifp->if_baudrate =
24232316Syokota		(csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
24332316Syokota
24432316Syokota	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
2452088Ssos	ether_ifattach(ifp, sc->arpcom.ac_enaddr);
2462088Ssos	/* device attach does transition from UNCONFIGURED to IDLE state */
2472088Ssos
2482088Ssos	if_printf(ifp, "speed %ld, address %6D, rxl ",
2492088Ssos	       ifp->if_baudrate, sc->arpcom.ac_enaddr, ":");
2502088Ssos	if (sc->delta_rxl)
2512088Ssos		printf("auto\n");
2522088Ssos	else
2532088Ssos		printf("%d (fixed)\n", sc->cur_rxl_index);
2542088Ssos}
2552088Ssos
2562088Ssos/* -------------------------------------------------------------------------- */
2572088Ssos
2582088Ssosstatic void
25932316Syokotasbni_init(void *xsc)
2602088Ssos{
2612088Ssos	struct sbni_softc *sc;
26232316Syokota	struct ifnet *ifp;
26332316Syokota	int  s;
2642088Ssos
2652088Ssos	sc = (struct sbni_softc *)xsc;
26632316Syokota	ifp = &sc->arpcom.ac_if;
26732316Syokota
26832316Syokota	/* address not known */
26932316Syokota	if (TAILQ_EMPTY(&ifp->if_addrhead))
27032316Syokota		return;
27132316Syokota
2722088Ssos	/*
27332316Syokota	 * kludge to avoid multiple initialization when more than once
27432316Syokota	 * protocols configured
27532316Syokota	 */
27632316Syokota	if (ifp->if_flags & IFF_RUNNING)
27732316Syokota		return;
27832316Syokota
27932316Syokota	s = splimp();
28032316Syokota	ifp->if_timer = 0;
28132316Syokota	card_start(sc);
28232316Syokota	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
28332316Syokota
28432316Syokota	ifp->if_flags |= IFF_RUNNING;
28532316Syokota	ifp->if_flags &= ~IFF_OACTIVE;
28632316Syokota
28732316Syokota	/* attempt to start output */
28832316Syokota	sbni_start(ifp);
28932316Syokota	splx(s);
29032316Syokota}
29132316Syokota
29232316Syokota
29332316Syokotastatic void
29432316Syokotasbni_start(struct ifnet *ifp)
29532316Syokota{
29632316Syokota	struct sbni_softc *sc = ifp->if_softc;
29732316Syokota	if (sc->tx_frameno == 0)
29832316Syokota		prepare_to_send(sc);
29932316Syokota}
30032316Syokota
30132316Syokota
30232316Syokotastatic void
30332316Syokotasbni_stop(struct sbni_softc *sc)
30432316Syokota{
30532316Syokota	sbni_outb(sc, CSR0, 0);
30632316Syokota	drop_xmit_queue(sc);
30732316Syokota
30832316Syokota	if (sc->rx_buf_p) {
30932316Syokota		m_freem(sc->rx_buf_p);
31032316Syokota		sc->rx_buf_p = NULL;
31132316Syokota	}
31232316Syokota
31332316Syokota	untimeout(sbni_timeout, sc, sc->wch);
31432316Syokota	sc->wch.callout = NULL;
31532316Syokota}
31632316Syokota
31732316Syokota/* -------------------------------------------------------------------------- */
31832316Syokota
31932316Syokota/* interrupt handler */
32032316Syokota
32132316Syokota/*
32232316Syokota * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
32332316Syokota * be looked as two independent single-channel devices. Every channel seems
32432316Syokota * as Ethernet interface but interrupt handler must be common. Really, first
32532316Syokota * channel ("master") driver only registers the handler. In it's struct softc
32632316Syokota * it has got pointer to "slave" channel's struct softc and handles that's
32729603Scharnier * interrupts too.
3282088Ssos *	softc of successfully attached ISA SBNI boards is linked to list.
3292088Ssos * While next board driver is initialized, it scans this list. If one
3302088Ssos * has found softc with same irq and ioaddr different by 4 then it assumes
3312088Ssos * this board to be "master".
3322088Ssos */
3332088Ssos
3348857Srgrimesvoid
3352088Ssossbni_intr(void *arg)
3362088Ssos{
3372088Ssos	struct sbni_softc *sc;
3382088Ssos	int repeat;
3392088Ssos
3402088Ssos	sc = (struct sbni_softc *)arg;
3412088Ssos
3422088Ssos	do {
3432088Ssos		repeat = 0;
3442088Ssos		if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
3452088Ssos			handle_channel(sc);
3462088Ssos			repeat = 1;
3472088Ssos		}
3482088Ssos		if (sc->slave_sc && 	/* second channel present */
3492088Ssos		    (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY))) {
3502088Ssos			handle_channel(sc->slave_sc);
3512088Ssos			repeat = 1;
3522088Ssos		}
3532088Ssos	} while (repeat);
3542088Ssos}
3552088Ssos
3562088Ssos
3572088Ssosstatic void
3582088Ssoshandle_channel(struct sbni_softc *sc)
3592088Ssos{
3602088Ssos	int req_ans;
3612088Ssos	u_char csr0;
3622088Ssos
3632088Ssos	sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
3642088Ssos
3652088Ssos	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
3662088Ssos	for (;;) {
3672088Ssos		csr0 = sbni_inb(sc, CSR0);
3682088Ssos		if ((csr0 & (RC_RDY | TR_RDY)) == 0)
3692088Ssos			break;
3702088Ssos
3712088Ssos		req_ans = !(sc->state & FL_PREV_OK);
3722088Ssos
3732088Ssos		if (csr0 & RC_RDY)
3742088Ssos			req_ans = recv_frame(sc);
3752088Ssos
3762088Ssos		/*
3772088Ssos		 * TR_RDY always equals 1 here because we have owned the marker,
3782088Ssos		 * and we set TR_REQ when disabled interrupts
3792088Ssos		 */
3802088Ssos		csr0 = sbni_inb(sc, CSR0);
3812088Ssos		if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
3822088Ssos			printf("sbni: internal error!\n");
3832088Ssos
38432316Syokota		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
38532316Syokota		if (req_ans || sc->tx_frameno != 0)
38632316Syokota			send_frame(sc);
38738053Syokota		else {
38838053Syokota			/* send the marker without any data */
38938053Syokota			sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
3902088Ssos		}
3912088Ssos	}
3928857Srgrimes
3932088Ssos	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
3948857Srgrimes}
3952088Ssos
39632316Syokota
39732316Syokota/*
3982088Ssos * Routine returns 1 if it need to acknoweledge received frame.
3998857Srgrimes * Empty frame received without errors won't be acknoweledged.
4002088Ssos */
40132316Syokota
4022088Ssosstatic int
4032088Ssosrecv_frame(struct sbni_softc *sc)
4042088Ssos{
4058857Srgrimes	u_int32_t crc;
4062088Ssos	u_int framelen, frameno, ack;
4078857Srgrimes	u_int is_first, frame_ok;
4089202Srgrimes
4098857Srgrimes	crc = CRC32_INITIAL;
4102088Ssos	if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
4118857Srgrimes		frame_ok = framelen > 4 ?
4122088Ssos		    upload_data(sc, framelen, frameno, is_first, crc) :
4138857Srgrimes		    skip_tail(sc, framelen, crc);
4142088Ssos		if (frame_ok)
4152088Ssos			interpret_ack(sc, ack);
4162088Ssos	} else
4172088Ssos		frame_ok = 0;
4182088Ssos
4192088Ssos	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
4202088Ssos	if (frame_ok) {
4212088Ssos		sc->state |= FL_PREV_OK;
42229603Scharnier		if (framelen > 4)
4232088Ssos			sc->in_stats.all_rx_number++;
4242088Ssos	} else {
4252088Ssos		sc->state &= ~FL_PREV_OK;
4262088Ssos		change_level(sc);
4272088Ssos		sc->in_stats.all_rx_number++;
4282088Ssos		sc->in_stats.bad_rx_number++;
4292088Ssos	}
4302088Ssos
4312088Ssos	return (!frame_ok || framelen > 4);
4322088Ssos}
4332088Ssos
4342088Ssos
4358857Srgrimesstatic void
4362088Ssossend_frame(struct sbni_softc *sc)
4372088Ssos{
4382088Ssos	u_int32_t crc;
4392088Ssos	u_char csr0;
4402088Ssos
4412088Ssos	crc = CRC32_INITIAL;
4422088Ssos	if (sc->state & FL_NEED_RESEND) {
4432088Ssos
4442088Ssos		/* if frame was sended but not ACK'ed - resend it */
4452088Ssos		if (sc->trans_errors) {
4462088Ssos			sc->trans_errors--;
4472088Ssos			if (sc->framelen != 0)
4482088Ssos				sc->in_stats.resend_tx_number++;
4496046Ssos		} else {
4506046Ssos			/* cannot xmit with many attempts */
4516046Ssos			drop_xmit_queue(sc);
4528857Srgrimes			goto do_send;
4532088Ssos		}
4542088Ssos	} else
45532316Syokota		sc->trans_errors = TR_ERROR_COUNT;
45632316Syokota
45732316Syokota	send_frame_header(sc, &crc);
45832316Syokota	sc->state |= FL_NEED_RESEND;
45932316Syokota	/*
4602088Ssos	 * FL_NEED_RESEND will be cleared after ACK, but if empty
46132316Syokota	 * frame sended then in prepare_to_send next frame
46232316Syokota	 */
46332316Syokota
46432316Syokota
46532316Syokota	if (sc->framelen) {
46632316Syokota		download_data(sc, &crc);
46732316Syokota		sc->in_stats.all_tx_number++;
46832316Syokota		sc->state |= FL_WAIT_ACK;
46932316Syokota	}
47032316Syokota
47132316Syokota	sbni_outsb(sc, (u_char *)&crc, sizeof crc);
47232316Syokota
47332316Syokotado_send:
47432316Syokota	csr0 = sbni_inb(sc, CSR0);
47532316Syokota	sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
47632316Syokota
47732316Syokota	if (sc->tx_frameno) {
47832316Syokota		/* next frame exists - request to send */
47932316Syokota		sbni_outb(sc, CSR0, csr0 | TR_REQ);
48032316Syokota	}
48132316Syokota}
48232316Syokota
48332316Syokota
48432316Syokotastatic void
48532316Syokotadownload_data(struct sbni_softc *sc, u_int32_t *crc_p)
48632316Syokota{
48732316Syokota	struct mbuf *m;
48832316Syokota	caddr_t	data_p;
48932316Syokota	u_int data_len, pos, slice;
49032316Syokota
49132316Syokota	data_p = NULL;		/* initialized to avoid warn */
49232316Syokota	pos = 0;
49332316Syokota
49432316Syokota	for (m = sc->tx_buf_p;  m != NULL && pos < sc->pktlen;  m = m->m_next) {
49532316Syokota		if (pos + m->m_len > sc->outpos) {
4962088Ssos			data_len = m->m_len - (sc->outpos - pos);
49732316Syokota			data_p = mtod(m, caddr_t) + (sc->outpos - pos);
49832316Syokota
49932316Syokota			goto do_copy;
50032316Syokota		} else
50132316Syokota			pos += m->m_len;
50232316Syokota	}
50332316Syokota
50432316Syokota	data_len = 0;
50532316Syokota
50632316Syokotado_copy:
50732316Syokota	pos = 0;
50832316Syokota	do {
50932316Syokota		if (data_len) {
51032316Syokota			slice = min(data_len, sc->framelen - pos);
51132316Syokota			sbni_outsb(sc, data_p, slice);
51232316Syokota			*crc_p = calc_crc32(*crc_p, data_p, slice);
51332316Syokota
51432316Syokota			pos += slice;
51532316Syokota			if (data_len -= slice)
51632316Syokota				data_p += slice;
51732316Syokota			else {
51832316Syokota				do {
51932316Syokota					m = m->m_next;
52032316Syokota				} while (m != NULL && m->m_len == 0);
52132316Syokota
52232316Syokota				if (m) {
52332316Syokota					data_len = m->m_len;
52432316Syokota					data_p = mtod(m, caddr_t);
52532316Syokota				}
52632316Syokota			}
52732316Syokota		} else {
52832316Syokota			/* frame too short - zero padding */
52932316Syokota
53032316Syokota			pos = sc->framelen - pos;
53132316Syokota			while (pos--) {
53232316Syokota				sbni_outb(sc, DAT, 0);
53332316Syokota				*crc_p = CRC32(0, *crc_p);
53432316Syokota			}
53532316Syokota			return;
53632316Syokota		}
53732316Syokota	} while (pos < sc->framelen);
53832316Syokota}
53932316Syokota
54032316Syokota
54132316Syokotastatic int
54232316Syokotaupload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
54332316Syokota	    u_int is_first, u_int32_t crc)
54432316Syokota{
54532316Syokota	int frame_ok;
54632316Syokota
54732316Syokota	if (is_first) {
54832316Syokota		sc->wait_frameno = frameno;
54932316Syokota		sc->inppos = 0;
55032316Syokota	}
55132316Syokota
55232316Syokota	if (sc->wait_frameno == frameno) {
55332316Syokota
55432316Syokota		if (sc->inppos + framelen  <=  ETHER_MAX_LEN) {
55532316Syokota			frame_ok = append_frame_to_pkt(sc, framelen, crc);
55638053Syokota
55738053Syokota		/*
55838053Syokota		 * if CRC is right but framelen incorrect then transmitter
55932316Syokota		 * error was occured... drop entire packet
56032316Syokota		 */
56132316Syokota		} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
56232316Syokota			sc->wait_frameno = 0;
56332486Syokota			sc->inppos = 0;
56432316Syokota			sc->arpcom.ac_if.if_ierrors++;
56532316Syokota			/* now skip all frames until is_first != 0 */
56632316Syokota		}
56732316Syokota	} else
56832316Syokota		frame_ok = skip_tail(sc, framelen, crc);
56932316Syokota
57032316Syokota	if (is_first && !frame_ok) {
57132316Syokota		/*
57232316Syokota		 * Frame has been violated, but we have stored
57332316Syokota		 * is_first already... Drop entire packet.
57432316Syokota		 */
57532316Syokota		sc->wait_frameno = 0;
57632316Syokota		sc->arpcom.ac_if.if_ierrors++;
57732316Syokota	}
57832316Syokota
57932316Syokota	return (frame_ok);
58032316Syokota}
58132316Syokota
58232316Syokota
58332316Syokotastatic __inline void	send_complete(struct sbni_softc *);
58432316Syokota
58532316Syokotastatic __inline void
58632486Syokotasend_complete(struct sbni_softc *sc)
58732316Syokota{
58832316Syokota	m_freem(sc->tx_buf_p);
58932486Syokota	sc->tx_buf_p = NULL;
59032486Syokota	sc->arpcom.ac_if.if_opackets++;
59132486Syokota}
59232316Syokota
59332316Syokota
59432316Syokotastatic void
59532486Syokotainterpret_ack(struct sbni_softc *sc, u_int ack)
59632316Syokota{
59732316Syokota	if (ack == FRAME_SENT_OK) {
59832316Syokota		sc->state &= ~FL_NEED_RESEND;
59932316Syokota
60032316Syokota		if (sc->state & FL_WAIT_ACK) {
60132316Syokota			sc->outpos += sc->framelen;
60232486Syokota
60332316Syokota			if (--sc->tx_frameno) {
60432316Syokota				sc->framelen = min(
60532316Syokota				    sc->maxframe, sc->pktlen - sc->outpos);
60632486Syokota			} else {
60732316Syokota				send_complete(sc);
60832316Syokota				prepare_to_send(sc);
60932316Syokota			}
61032316Syokota		}
61132316Syokota	}
61232316Syokota
61332316Syokota	sc->state &= ~FL_WAIT_ACK;
61432316Syokota}
61532486Syokota
61632316Syokota
61732486Syokota/*
61832486Syokota * Glue received frame with previous fragments of packet.
61932486Syokota * Indicate packet when last frame would be accepted.
62032486Syokota */
62132486Syokota
62232316Syokotastatic int
62332316Syokotaappend_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
62432316Syokota{
62532316Syokota	caddr_t p;
62632316Syokota
62732316Syokota	if (sc->inppos + framelen > ETHER_MAX_LEN)
62832316Syokota		return (0);
62932316Syokota
63032316Syokota	if (!sc->rx_buf_p && !get_rx_buf(sc))
63132316Syokota		return (0);
63232316Syokota
63332316Syokota	p = sc->rx_buf_p->m_data + sc->inppos;
63432316Syokota	sbni_insb(sc, p, framelen);
63532316Syokota	if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
63632316Syokota		return (0);
63732316Syokota
63832316Syokota	sc->inppos += framelen - 4;
63932316Syokota	if (--sc->wait_frameno == 0) {		/* last frame received */
64032316Syokota		indicate_pkt(sc);
64132316Syokota		sc->arpcom.ac_if.if_ipackets++;
64232316Syokota	}
64332316Syokota
64432316Syokota	return (1);
64532316Syokota}
64632316Syokota
64732316Syokota
64832316Syokota/*
64932316Syokota * Prepare to start output on adapter. Current priority must be set to splimp
65032486Syokota * before this routine is called.
65132316Syokota * Transmitter will be actually activated when marker has been accepted.
65232316Syokota */
65332316Syokota
65419569Sjoergstatic void
6552088Ssosprepare_to_send(struct sbni_softc *sc)
65632316Syokota{
65732316Syokota	struct mbuf *m;
6582088Ssos	u_int len;
65932316Syokota
66019569Sjoerg	/* sc->tx_buf_p == NULL here! */
66135750Sdes	if (sc->tx_buf_p)
66235750Sdes		printf("sbni: memory leak!\n");
6632088Ssos
6642088Ssos	sc->outpos = 0;
6652088Ssos	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
66629603Scharnier
6672088Ssos	for (;;) {
6682088Ssos		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, sc->tx_buf_p);
6692088Ssos		if (!sc->tx_buf_p) {
67029603Scharnier			/* nothing to transmit... */
6712088Ssos			sc->pktlen     = 0;
6722088Ssos			sc->tx_frameno = 0;
67332316Syokota			sc->framelen   = 0;
67432316Syokota			sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
67532316Syokota			return;
6762088Ssos		}
67732316Syokota
6782088Ssos		for (len = 0, m = sc->tx_buf_p;  m;  m = m->m_next)
6792088Ssos			len += m->m_len;
68019569Sjoerg
68119569Sjoerg		if (len != 0)
68219569Sjoerg			break;
68319569Sjoerg		m_freem(sc->tx_buf_p);
68432316Syokota	}
68532316Syokota
68632316Syokota	if (len < SBNI_MIN_LEN)
68732316Syokota		len = SBNI_MIN_LEN;
68832316Syokota
68932316Syokota	sc->pktlen	= len;
69019569Sjoerg	sc->tx_frameno	= (len + sc->maxframe - 1) / sc->maxframe;
69119569Sjoerg	sc->framelen	= min(len, sc->maxframe);
69232316Syokota
69329603Scharnier	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
6942088Ssos	sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
6952088Ssos	BPF_MTAP(&sc->arpcom.ac_if, sc->tx_buf_p);
6962088Ssos}
69732316Syokota
69832316Syokota
69932316Syokotastatic void
70032316Syokotadrop_xmit_queue(struct sbni_softc *sc)
70132316Syokota{
70232316Syokota	struct mbuf *m;
7032088Ssos
7042088Ssos	if (sc->tx_buf_p) {
7052088Ssos		m_freem(sc->tx_buf_p);
7062088Ssos		sc->tx_buf_p = NULL;
7072088Ssos		sc->arpcom.ac_if.if_oerrors++;
70832316Syokota	}
70932316Syokota
7102088Ssos	for (;;) {
7112088Ssos		IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m);
71232316Syokota		if (m == NULL)
71329603Scharnier			break;
71432316Syokota		m_freem(m);
71532316Syokota		sc->arpcom.ac_if.if_oerrors++;
7162088Ssos	}
7172088Ssos
7182088Ssos	sc->tx_frameno	= 0;
7192088Ssos	sc->framelen	= 0;
7202088Ssos	sc->outpos	= 0;
7212088Ssos	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
72232316Syokota	sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
72332316Syokota}
72432316Syokota
72532316Syokota
72632316Syokotastatic void
72732316Syokotasend_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
72832316Syokota{
7292088Ssos	u_int32_t crc;
7302088Ssos	u_int len_field;
7312088Ssos	u_char value;
7322088Ssos
7332088Ssos	crc = *crc_p;
7342088Ssos	len_field = sc->framelen + 6;	/* CRC + frameno + reserved */
7352088Ssos
7362088Ssos	if (sc->state & FL_NEED_RESEND)
7372088Ssos		len_field |= FRAME_RETRY;	/* non-first attempt... */
7382088Ssos
7392088Ssos	if (sc->outpos == 0)
7402088Ssos		len_field |= FRAME_FIRST;
7412088Ssos
7422088Ssos	len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
74329603Scharnier	sbni_outb(sc, DAT, SBNI_SIG);
7442088Ssos
7452088Ssos	value = (u_char)len_field;
7462088Ssos	sbni_outb(sc, DAT, value);
7472088Ssos	crc = CRC32(value, crc);
7482088Ssos	value = (u_char)(len_field >> 8);
7492088Ssos	sbni_outb(sc, DAT, value);
7502088Ssos	crc = CRC32(value, crc);
7512088Ssos
7522088Ssos	sbni_outb(sc, DAT, sc->tx_frameno);
7532088Ssos	crc = CRC32(sc->tx_frameno, crc);
7542088Ssos	sbni_outb(sc, DAT, 0);
7552088Ssos	crc = CRC32(0, crc);
7562088Ssos	*crc_p = crc;
7572088Ssos}
75829603Scharnier
7592088Ssos
7602088Ssos/*
7612088Ssos * if frame tail not needed (incorrect number or received twice),
7622088Ssos * it won't store, but CRC will be calculated
76329603Scharnier */
7642088Ssos
7652088Ssosstatic int
7662088Ssosskip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
7672088Ssos{
7682088Ssos	while (tail_len--)
7692088Ssos		crc = CRC32(sbni_inb(sc, DAT), crc);
77029603Scharnier
7712088Ssos	return (crc == CRC32_REMAINDER);
7722088Ssos}
7732088Ssos
7742088Ssos
7752088Ssosstatic int
7762088Ssoscheck_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
7775536Ssos	   u_int *ack, u_int *is_first, u_int32_t *crc_p)
7782088Ssos{
77938044Syokota	u_int32_t crc;
78038044Syokota	u_char value;
78138044Syokota
78238044Syokota	crc = *crc_p;
78338044Syokota	if (sbni_inb(sc, DAT) != SBNI_SIG)
7848857Srgrimes		return (0);
78538044Syokota
7865536Ssos	value = sbni_inb(sc, DAT);
78738044Syokota	*framelen = (u_int)value;
7882088Ssos	crc = CRC32(value, crc);
7892088Ssos	value = sbni_inb(sc, DAT);
7908857Srgrimes	*framelen |= ((u_int)value) << 8;
7915536Ssos	crc = CRC32(value, crc);
7922088Ssos
7932088Ssos	*ack = *framelen & FRAME_ACK_MASK;
7942088Ssos	*is_first = (*framelen & FRAME_FIRST) != 0;
7952088Ssos
7962088Ssos	if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
7972088Ssos		return (0);
7982088Ssos
79929603Scharnier	value = sbni_inb(sc, DAT);
8002088Ssos	*frameno = (u_int)value;
8012088Ssos	crc = CRC32(value, crc);
80238044Syokota
80338044Syokota	crc = CRC32(sbni_inb(sc, DAT), crc);		/* reserved byte */
80438044Syokota	*framelen -= 2;
8052088Ssos
8062088Ssos	*crc_p = crc;
8075536Ssos	return (1);
80838044Syokota}
8095536Ssos
8102088Ssos
8112088Ssosstatic int
8122088Ssosget_rx_buf(struct sbni_softc *sc)
8132088Ssos{
8142088Ssos	struct mbuf *m;
8152088Ssos
8162088Ssos	MGETHDR(m, M_DONTWAIT, MT_DATA);
8172088Ssos	if (m == NULL) {
8182088Ssos		if_printf(&sc->arpcom.ac_if, "cannot allocate header mbuf\n");
8192088Ssos		return (0);
8202088Ssos	}
8212088Ssos
8222088Ssos	/*
8232088Ssos	 * We always put the received packet in a single buffer -
8242088Ssos	 * either with just an mbuf header or in a cluster attached
8252088Ssos	 * to the header. The +2 is to compensate for the alignment
8262088Ssos	 * fixup below.
8272088Ssos	 */
8282088Ssos	if (ETHER_MAX_LEN + 2 > MHLEN) {
8292088Ssos		/* Attach an mbuf cluster */
8302088Ssos		MCLGET(m, M_DONTWAIT);
8312088Ssos		if ((m->m_flags & M_EXT) == 0) {
8322088Ssos			m_freem(m);
8332088Ssos			return (0);
8342088Ssos		}
8352088Ssos	}
8362088Ssos	m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
8372088Ssos
8382088Ssos	/*
8392088Ssos	 * The +2 is to longword align the start of the real packet.
84029603Scharnier	 * (sizeof ether_header == 14)
8412088Ssos	 * This is important for NFS.
8422088Ssos	 */
8432088Ssos	m_adj(m, 2);
8442088Ssos	sc->rx_buf_p = m;
8452088Ssos	return (1);
8462088Ssos}
8472088Ssos
8482088Ssos
8492088Ssosstatic void
8502088Ssosindicate_pkt(struct sbni_softc *sc)
8512088Ssos{
8522088Ssos	struct ifnet *ifp = &sc->arpcom.ac_if;
8532088Ssos	struct mbuf *m;
85429603Scharnier
8552088Ssos	m = sc->rx_buf_p;
8562088Ssos	m->m_pkthdr.rcvif = ifp;
8572088Ssos	m->m_pkthdr.len   = m->m_len = sc->inppos;
8586046Ssos
8596046Ssos	(*ifp->if_input)(ifp, m);
8606046Ssos	sc->rx_buf_p = NULL;
8616046Ssos}
8626046Ssos
8636046Ssos/* -------------------------------------------------------------------------- */
8646046Ssos
86529603Scharnier/*
8666046Ssos * Routine checks periodically wire activity and regenerates marker if
8676046Ssos * connect was inactive for a long time.
8686046Ssos */
86929603Scharnier
8706046Ssosstatic void
8716046Ssossbni_timeout(void *xsc)
8726046Ssos{
87329603Scharnier	struct sbni_softc *sc;
8742088Ssos	int s;
8752088Ssos	u_char csr0;
87629603Scharnier
87738044Syokota	sc = (struct sbni_softc *)xsc;
87829603Scharnier	s = splimp();
87929603Scharnier
88029603Scharnier	csr0 = sbni_inb(sc, CSR0);
8812088Ssos	if (csr0 & RC_CHK) {
8822088Ssos
8832088Ssos		if (sc->timer_ticks) {
8842088Ssos			if (csr0 & (RC_RDY | BU_EMP))
8852088Ssos				/* receiving not active */
8862088Ssos				sc->timer_ticks--;
8872088Ssos		} else {
8882088Ssos			sc->in_stats.timeout_number++;
88919569Sjoerg			if (sc->delta_rxl)
8902088Ssos				timeout_change_level(sc);
8912088Ssos
8922088Ssos			sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
8932088Ssos			csr0 = sbni_inb(sc, CSR0);
8942088Ssos		}
8952088Ssos	}
8962088Ssos
8972088Ssos	sbni_outb(sc, CSR0, csr0 | RC_CHK);
89819569Sjoerg	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
8992088Ssos	splx(s);
90019569Sjoerg}
90119569Sjoerg
90219569Sjoerg/* -------------------------------------------------------------------------- */
9032088Ssos
9048857Srgrimesstatic void
9052088Ssoscard_start(struct sbni_softc *sc)
9062088Ssos{
9072088Ssos	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
9082088Ssos	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
9092088Ssos	sc->state |= FL_PREV_OK;
9106046Ssos
9116046Ssos	sc->inppos = 0;
9126046Ssos	sc->wait_frameno = 0;
9132088Ssos
9142088Ssos	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
9152088Ssos	sbni_outb(sc, CSR0, EN_INT);
9162088Ssos}
9172088Ssos
9182088Ssos/* -------------------------------------------------------------------------- */
9192088Ssos
9202088Ssos/*
9212088Ssos * Device timeout/watchdog routine. Entered if the device neglects to
92229603Scharnier *	generate an interrupt after a transmit has been started on it.
9232088Ssos */
9242088Ssos
9252088Ssosstatic void
9262088Ssossbni_watchdog(struct ifnet *ifp)
9272088Ssos{
928	log(LOG_ERR, "%s: device timeout\n", ifp->if_xname);
929	ifp->if_oerrors++;
930}
931
932
933static u_char rxl_tab[] = {
934	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
935	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
936};
937
938#define SIZE_OF_TIMEOUT_RXL_TAB 4
939static u_char timeout_rxl_tab[] = {
940	0x03, 0x05, 0x08, 0x0b
941};
942
943static void
944set_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
945{
946	if (flags.fixed_rxl) {
947		sc->delta_rxl = 0; /* disable receive level autodetection */
948		sc->cur_rxl_index = flags.rxl;
949	} else {
950		sc->delta_rxl = DEF_RXL_DELTA;
951		sc->cur_rxl_index = DEF_RXL;
952	}
953
954	sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
955	sc->csr1.rxl  = rxl_tab[sc->cur_rxl_index];
956	sc->maxframe  = DEFAULT_FRAME_LEN;
957
958	/*
959	 * generate Ethernet address (0x00ff01xxxxxx)
960	 */
961	*(u_int16_t *) sc->arpcom.ac_enaddr = htons(0x00ff);
962	if (flags.mac_addr) {
963		*(u_int32_t *) (sc->arpcom.ac_enaddr + 2) =
964		    htonl(flags.mac_addr | 0x01000000);
965	} else {
966		*(u_char *) (sc->arpcom.ac_enaddr + 2) = 0x01;
967		read_random(sc->arpcom.ac_enaddr + 3, 3);
968	}
969}
970
971
972#ifdef SBNI_DUAL_COMPOUND
973
974struct sbni_softc *
975connect_to_master(struct sbni_softc *sc)
976{
977	struct sbni_softc *p, *p_prev;
978
979	for (p = sbni_headlist, p_prev = NULL; p; p_prev = p, p = p->link) {
980		if (rman_get_start(p->io_res) == rman_get_start(sc->io_res) + 4 ||
981		    rman_get_start(p->io_res) == rman_get_start(sc->io_res) - 4) {
982			p->slave_sc = sc;
983			if (p_prev)
984				p_prev->link = p->link;
985			else
986				sbni_headlist = p->link;
987			return p;
988		}
989	}
990
991	return (NULL);
992}
993
994#endif	/* SBNI_DUAL_COMPOUND */
995
996
997/* Receive level auto-selection */
998
999static void
1000change_level(struct sbni_softc *sc)
1001{
1002	if (sc->delta_rxl == 0)		/* do not auto-negotiate RxL */
1003		return;
1004
1005	if (sc->cur_rxl_index == 0)
1006		sc->delta_rxl = 1;
1007	else if (sc->cur_rxl_index == 15)
1008		sc->delta_rxl = -1;
1009	else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
1010		sc->delta_rxl = -sc->delta_rxl;
1011
1012	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
1013	sbni_inb(sc, CSR0);	/* it needed for PCI cards */
1014	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
1015
1016	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
1017	sc->cur_rxl_rcvd  = 0;
1018}
1019
1020
1021static void
1022timeout_change_level(struct sbni_softc *sc)
1023{
1024	sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
1025	if (++sc->timeout_rxl >= 4)
1026		sc->timeout_rxl = 0;
1027
1028	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
1029	sbni_inb(sc, CSR0);
1030	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
1031
1032	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
1033	sc->cur_rxl_rcvd  = 0;
1034}
1035
1036/* -------------------------------------------------------------------------- */
1037
1038/*
1039 * Process an ioctl request. This code needs some work - it looks
1040 *	pretty ugly.
1041 */
1042
1043static int
1044sbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
1045{
1046	struct sbni_softc *sc;
1047	struct ifreq *ifr;
1048	struct thread *td;
1049	struct sbni_in_stats *in_stats;
1050	struct sbni_flags flags;
1051	int error, s;
1052
1053	sc = ifp->if_softc;
1054	ifr = (struct ifreq *)data;
1055	td = curthread;
1056	error = 0;
1057
1058	s = splimp();
1059
1060	switch (command) {
1061	case SIOCSIFFLAGS:
1062		/*
1063		 * If the interface is marked up and stopped, then start it.
1064		 * If it is marked down and running, then stop it.
1065		 */
1066		if (ifp->if_flags & IFF_UP) {
1067			if (!(ifp->if_flags & IFF_RUNNING))
1068				sbni_init(sc);
1069		} else {
1070			if (ifp->if_flags & IFF_RUNNING) {
1071				sbni_stop(sc);
1072				ifp->if_flags &= ~IFF_RUNNING;
1073			}
1074		}
1075		break;
1076
1077	case SIOCADDMULTI:
1078	case SIOCDELMULTI:
1079		/*
1080		 * Multicast list has changed; set the hardware filter
1081		 * accordingly.
1082		 */
1083		error = 0;
1084		/* if (ifr == NULL)
1085			error = EAFNOSUPPORT; */
1086		break;
1087
1088	case SIOCSIFMTU:
1089		if (ifr->ifr_mtu > ETHERMTU)
1090			error = EINVAL;
1091		else
1092			ifp->if_mtu = ifr->ifr_mtu;
1093		break;
1094
1095		/*
1096		 * SBNI specific ioctl
1097		 */
1098	case SIOCGHWFLAGS:	/* get flags */
1099		bcopy((caddr_t) sc->arpcom.ac_enaddr+3, (caddr_t) &flags, 3);
1100		flags.rxl = sc->cur_rxl_index;
1101		flags.rate = sc->csr1.rate;
1102		flags.fixed_rxl = (sc->delta_rxl == 0);
1103		flags.fixed_rate = 1;
1104		ifr->ifr_data = *(caddr_t*) &flags;
1105		break;
1106
1107	case SIOCGINSTATS:
1108		in_stats = (struct sbni_in_stats *)ifr->ifr_data;
1109		bcopy((void *)(&(sc->in_stats)), (void *)in_stats,
1110		      sizeof(struct sbni_in_stats));
1111		break;
1112
1113	case SIOCSHWFLAGS:	/* set flags */
1114		/* root only */
1115		error = suser(td);
1116		if (error)
1117			break;
1118		flags = *(struct sbni_flags*)&ifr->ifr_data;
1119		if (flags.fixed_rxl) {
1120			sc->delta_rxl = 0;
1121			sc->cur_rxl_index = flags.rxl;
1122		} else {
1123			sc->delta_rxl = DEF_RXL_DELTA;
1124			sc->cur_rxl_index = DEF_RXL;
1125		}
1126		sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
1127		sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
1128		if (flags.mac_addr)
1129			bcopy((caddr_t) &flags,
1130			      (caddr_t) sc->arpcom.ac_enaddr+3, 3);
1131
1132		/* Don't be afraid... */
1133		sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
1134		break;
1135
1136	case SIOCRINSTATS:
1137		if (!(error = suser(td)))	/* root only */
1138			bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
1139		break;
1140
1141	default:
1142		error = ether_ioctl(ifp, command, data);
1143		break;
1144	}
1145
1146	splx(s);
1147	return (error);
1148}
1149
1150/* -------------------------------------------------------------------------- */
1151
1152#ifdef ASM_CRC
1153
1154static u_int32_t
1155calc_crc32(u_int32_t crc, caddr_t p, u_int len)
1156{
1157	register u_int32_t  _crc __asm ("ax");
1158	_crc = crc;
1159
1160	__asm __volatile (
1161		"xorl	%%ebx, %%ebx\n"
1162		"movl	%1, %%esi\n"
1163		"movl	%2, %%ecx\n"
1164		"movl	$crc32tab, %%edi\n"
1165		"shrl	$2, %%ecx\n"
1166		"jz	1f\n"
1167
1168		".align 4\n"
1169	"0:\n"
1170		"movb	%%al, %%bl\n"
1171		"movl	(%%esi), %%edx\n"
1172		"shrl	$8, %%eax\n"
1173		"xorb	%%dl, %%bl\n"
1174		"shrl	$8, %%edx\n"
1175		"xorl	(%%edi,%%ebx,4), %%eax\n"
1176
1177		"movb	%%al, %%bl\n"
1178		"shrl	$8, %%eax\n"
1179		"xorb	%%dl, %%bl\n"
1180		"shrl	$8, %%edx\n"
1181		"xorl	(%%edi,%%ebx,4), %%eax\n"
1182
1183		"movb	%%al, %%bl\n"
1184		"shrl	$8, %%eax\n"
1185		"xorb	%%dl, %%bl\n"
1186		"movb	%%dh, %%dl\n"
1187		"xorl	(%%edi,%%ebx,4), %%eax\n"
1188
1189		"movb	%%al, %%bl\n"
1190		"shrl	$8, %%eax\n"
1191		"xorb	%%dl, %%bl\n"
1192		"addl	$4, %%esi\n"
1193		"xorl	(%%edi,%%ebx,4), %%eax\n"
1194
1195		"decl	%%ecx\n"
1196		"jnz	0b\n"
1197
1198	"1:\n"
1199		"movl	%2, %%ecx\n"
1200		"andl	$3, %%ecx\n"
1201		"jz	2f\n"
1202
1203		"movb	%%al, %%bl\n"
1204		"shrl	$8, %%eax\n"
1205		"xorb	(%%esi), %%bl\n"
1206		"xorl	(%%edi,%%ebx,4), %%eax\n"
1207
1208		"decl	%%ecx\n"
1209		"jz	2f\n"
1210
1211		"movb	%%al, %%bl\n"
1212		"shrl	$8, %%eax\n"
1213		"xorb	1(%%esi), %%bl\n"
1214		"xorl	(%%edi,%%ebx,4), %%eax\n"
1215
1216		"decl	%%ecx\n"
1217		"jz	2f\n"
1218
1219		"movb	%%al, %%bl\n"
1220		"shrl	$8, %%eax\n"
1221		"xorb	2(%%esi), %%bl\n"
1222		"xorl	(%%edi,%%ebx,4), %%eax\n"
1223	"2:\n"
1224		: "=a" (_crc)
1225		: "g" (p), "g" (len)
1226		: "bx", "cx", "dx", "si", "di"
1227	);
1228
1229	return (_crc);
1230}
1231
1232#else	/* ASM_CRC */
1233
1234static u_int32_t
1235calc_crc32(u_int32_t crc, caddr_t p, u_int len)
1236{
1237	while (len--)
1238		crc = CRC32(*p++, crc);
1239
1240	return (crc);
1241}
1242
1243#endif	/* ASM_CRC */
1244
1245
1246static u_int32_t crc32tab[] __aligned(8) = {
1247	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
1248	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
1249	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
1250	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
1251	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
1252	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
1253	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
1254	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
1255	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
1256	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
1257	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
1258	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
1259	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
1260	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
1261	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
1262	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
1263	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
1264	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
1265	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
1266	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
1267	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
1268	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
1269	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
1270	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
1271	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
1272	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
1273	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
1274	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
1275	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
1276	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
1277	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
1278	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
1279	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
1280	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
1281	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
1282	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
1283	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
1284	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
1285	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
1286	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
1287	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
1288	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
1289	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
1290	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
1291	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
1292	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
1293	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
1294	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
1295	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
1296	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
1297	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
1298	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
1299	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
1300	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
1301	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
1302	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
1303	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
1304	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
1305	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
1306	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
1307	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
1308	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
1309	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
1310	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
1311};
1312