xref: /freebsd/sys/dev/sbni/if_sbni.c (revision aa3860851b9f6a6002d135b1cac7736e0995eedc)
126e46883SJohn Baldwin /*-
226e46883SJohn Baldwin  * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
326e46883SJohn Baldwin  * Author: Denis I.Timofeev <timofeev@granch.ru>
426e46883SJohn Baldwin  *
526e46883SJohn Baldwin  * Redistributon and use in source and binary forms, with or without
626e46883SJohn Baldwin  * modification, are permitted provided that the following conditions
726e46883SJohn Baldwin  * are met:
826e46883SJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
926e46883SJohn Baldwin  *    notice unmodified, this list of conditions, and the following
1026e46883SJohn Baldwin  *    disclaimer.
1126e46883SJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
1226e46883SJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
1326e46883SJohn Baldwin  *    documentation and/or other materials provided with the distribution.
1426e46883SJohn Baldwin  *
1526e46883SJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1626e46883SJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1726e46883SJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1826e46883SJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1926e46883SJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2026e46883SJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2126e46883SJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2226e46883SJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2326e46883SJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
2426e46883SJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2526e46883SJohn Baldwin  * SUCH DAMAGE.
2626e46883SJohn Baldwin  *
2726e46883SJohn Baldwin  */
2826e46883SJohn Baldwin 
2926e46883SJohn Baldwin #include <sys/cdefs.h>
3026e46883SJohn Baldwin /*
3126e46883SJohn Baldwin  * Device driver for Granch SBNI12 leased line adapters
3226e46883SJohn Baldwin  *
3326e46883SJohn Baldwin  * Revision 2.0.0  1997/08/06
3426e46883SJohn Baldwin  * Initial revision by Alexey Zverev
3526e46883SJohn Baldwin  *
3626e46883SJohn Baldwin  * Revision 2.0.1 1997/08/11
3726e46883SJohn Baldwin  * Additional internal statistics support (tx statistics)
3826e46883SJohn Baldwin  *
3926e46883SJohn Baldwin  * Revision 2.0.2 1997/11/05
4026e46883SJohn Baldwin  * if_bpf bug has been fixed
4126e46883SJohn Baldwin  *
4226e46883SJohn Baldwin  * Revision 2.0.3 1998/12/20
4326e46883SJohn Baldwin  * Memory leakage has been eliminated in
4426e46883SJohn Baldwin  * the sbni_st and sbni_timeout routines.
4526e46883SJohn Baldwin  *
4626e46883SJohn Baldwin  * Revision 3.0 2000/08/10 by Yaroslav Polyakov
4726e46883SJohn Baldwin  * Support for PCI cards. 4.1 modification.
4826e46883SJohn Baldwin  *
4926e46883SJohn Baldwin  * Revision 3.1 2000/09/12
5026e46883SJohn Baldwin  * Removed extra #defines around bpf functions
5126e46883SJohn Baldwin  *
5226e46883SJohn Baldwin  * Revision 4.0 2000/11/23 by Denis Timofeev
5326e46883SJohn Baldwin  * Completely redesigned the buffer management
5426e46883SJohn Baldwin  *
5526e46883SJohn Baldwin  * Revision 4.1 2001/01/21
5626e46883SJohn Baldwin  * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
5726e46883SJohn Baldwin  *
5826e46883SJohn Baldwin  * Written with reference to NE2000 driver developed by David Greenman.
5926e46883SJohn Baldwin  */
6026e46883SJohn Baldwin 
6126e46883SJohn Baldwin #include <sys/param.h>
6226e46883SJohn Baldwin #include <sys/bus.h>
6326e46883SJohn Baldwin #include <sys/systm.h>
6426e46883SJohn Baldwin #include <sys/socket.h>
6526e46883SJohn Baldwin #include <sys/sockio.h>
6626e46883SJohn Baldwin #include <sys/mbuf.h>
6726e46883SJohn Baldwin #include <sys/kernel.h>
6826e46883SJohn Baldwin #include <sys/priv.h>
6926e46883SJohn Baldwin #include <sys/proc.h>
7026e46883SJohn Baldwin #include <sys/callout.h>
7126e46883SJohn Baldwin #include <sys/syslog.h>
7226e46883SJohn Baldwin #include <sys/random.h>
7326e46883SJohn Baldwin 
7426e46883SJohn Baldwin #include <machine/bus.h>
7526e46883SJohn Baldwin #include <sys/rman.h>
7626e46883SJohn Baldwin #include <machine/resource.h>
7726e46883SJohn Baldwin 
7826e46883SJohn Baldwin #include <net/if.h>
7976039bc8SGleb Smirnoff #include <net/if_var.h>
8026e46883SJohn Baldwin #include <net/if_dl.h>
8126e46883SJohn Baldwin #include <net/ethernet.h>
8226e46883SJohn Baldwin #include <net/bpf.h>
8326e46883SJohn Baldwin #include <net/if_types.h>
8426e46883SJohn Baldwin 
8526e46883SJohn Baldwin #include <dev/sbni/if_sbnireg.h>
8626e46883SJohn Baldwin #include <dev/sbni/if_sbnivar.h>
8726e46883SJohn Baldwin 
8826e46883SJohn Baldwin static void	sbni_init(void *);
8926e46883SJohn Baldwin static void	sbni_init_locked(struct sbni_softc *);
903fba06adSJustin Hibbits static void	sbni_start(if_t);
913fba06adSJustin Hibbits static void	sbni_start_locked(if_t);
923fba06adSJustin Hibbits static int	sbni_ioctl(if_t, u_long, caddr_t);
9326e46883SJohn Baldwin static void	sbni_stop(struct sbni_softc *);
9426e46883SJohn Baldwin static void	handle_channel(struct sbni_softc *);
9526e46883SJohn Baldwin 
9626e46883SJohn Baldwin static void	card_start(struct sbni_softc *);
9726e46883SJohn Baldwin static int	recv_frame(struct sbni_softc *);
9826e46883SJohn Baldwin static void	send_frame(struct sbni_softc *);
9926e46883SJohn Baldwin static int	upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
10026e46883SJohn Baldwin static int	skip_tail(struct sbni_softc *, u_int, u_int32_t);
10126e46883SJohn Baldwin static void	interpret_ack(struct sbni_softc *, u_int);
10226e46883SJohn Baldwin static void	download_data(struct sbni_softc *, u_int32_t *);
10326e46883SJohn Baldwin static void	prepare_to_send(struct sbni_softc *);
10426e46883SJohn Baldwin static void	drop_xmit_queue(struct sbni_softc *);
10526e46883SJohn Baldwin static int	get_rx_buf(struct sbni_softc *);
10626e46883SJohn Baldwin static void	indicate_pkt(struct sbni_softc *);
10726e46883SJohn Baldwin static void	change_level(struct sbni_softc *);
10826e46883SJohn Baldwin static int	check_fhdr(struct sbni_softc *, u_int *, u_int *,
10926e46883SJohn Baldwin 			   u_int *, u_int *, u_int32_t *);
11026e46883SJohn Baldwin static int	append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
11126e46883SJohn Baldwin static void	timeout_change_level(struct sbni_softc *);
11226e46883SJohn Baldwin static void	send_frame_header(struct sbni_softc *, u_int32_t *);
11326e46883SJohn Baldwin static void	set_initial_values(struct sbni_softc *, struct sbni_flags);
11426e46883SJohn Baldwin 
11526e46883SJohn Baldwin static u_int32_t	calc_crc32(u_int32_t, caddr_t, u_int);
1165773ac11SJohn Baldwin static callout_func_t	sbni_timeout;
11726e46883SJohn Baldwin 
11826e46883SJohn Baldwin static __inline u_char	sbni_inb(struct sbni_softc *, enum sbni_reg);
11926e46883SJohn Baldwin static __inline void	sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
12026e46883SJohn Baldwin static __inline void	sbni_insb(struct sbni_softc *, u_char *, u_int);
12126e46883SJohn Baldwin static __inline void	sbni_outsb(struct sbni_softc *, u_char *, u_int);
12226e46883SJohn Baldwin 
12326e46883SJohn Baldwin static u_int32_t crc32tab[];
12426e46883SJohn Baldwin 
12526e46883SJohn Baldwin #ifdef SBNI_DUAL_COMPOUND
12626e46883SJohn Baldwin static struct mtx headlist_lock;
12726e46883SJohn Baldwin MTX_SYSINIT(headlist_lock, &headlist_lock, "sbni headlist", MTX_DEF);
12826e46883SJohn Baldwin static struct sbni_softc *sbni_headlist;
12926e46883SJohn Baldwin #endif
13026e46883SJohn Baldwin 
13126e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
13226e46883SJohn Baldwin 
13326e46883SJohn Baldwin static __inline u_char
sbni_inb(struct sbni_softc * sc,enum sbni_reg reg)13426e46883SJohn Baldwin sbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
13526e46883SJohn Baldwin {
13626e46883SJohn Baldwin 	return bus_space_read_1(
13726e46883SJohn Baldwin 	    rman_get_bustag(sc->io_res),
13826e46883SJohn Baldwin 	    rman_get_bushandle(sc->io_res),
13926e46883SJohn Baldwin 	    sc->io_off + reg);
14026e46883SJohn Baldwin }
14126e46883SJohn Baldwin 
14226e46883SJohn Baldwin static __inline void
sbni_outb(struct sbni_softc * sc,enum sbni_reg reg,u_char value)14326e46883SJohn Baldwin sbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
14426e46883SJohn Baldwin {
14526e46883SJohn Baldwin 	bus_space_write_1(
14626e46883SJohn Baldwin 	    rman_get_bustag(sc->io_res),
14726e46883SJohn Baldwin 	    rman_get_bushandle(sc->io_res),
14826e46883SJohn Baldwin 	    sc->io_off + reg, value);
14926e46883SJohn Baldwin }
15026e46883SJohn Baldwin 
15126e46883SJohn Baldwin static __inline void
sbni_insb(struct sbni_softc * sc,u_char * to,u_int len)15226e46883SJohn Baldwin sbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
15326e46883SJohn Baldwin {
15426e46883SJohn Baldwin 	bus_space_read_multi_1(
15526e46883SJohn Baldwin 	    rman_get_bustag(sc->io_res),
15626e46883SJohn Baldwin 	    rman_get_bushandle(sc->io_res),
15726e46883SJohn Baldwin 	    sc->io_off + DAT, to, len);
15826e46883SJohn Baldwin }
15926e46883SJohn Baldwin 
16026e46883SJohn Baldwin static __inline void
sbni_outsb(struct sbni_softc * sc,u_char * from,u_int len)16126e46883SJohn Baldwin sbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
16226e46883SJohn Baldwin {
16326e46883SJohn Baldwin 	bus_space_write_multi_1(
16426e46883SJohn Baldwin 	    rman_get_bustag(sc->io_res),
16526e46883SJohn Baldwin 	    rman_get_bushandle(sc->io_res),
16626e46883SJohn Baldwin 	    sc->io_off + DAT, from, len);
16726e46883SJohn Baldwin }
16826e46883SJohn Baldwin 
16926e46883SJohn Baldwin /*
17026e46883SJohn Baldwin 	Valid combinations in CSR0 (for probing):
17126e46883SJohn Baldwin 
17226e46883SJohn Baldwin 	VALID_DECODER	0000,0011,1011,1010
17326e46883SJohn Baldwin 
17426e46883SJohn Baldwin 				    	; 0   ; -
17526e46883SJohn Baldwin 				TR_REQ	; 1   ; +
17626e46883SJohn Baldwin 			TR_RDY	    	; 2   ; -
17726e46883SJohn Baldwin 			TR_RDY	TR_REQ	; 3   ; +
17826e46883SJohn Baldwin 		BU_EMP		    	; 4   ; +
17926e46883SJohn Baldwin 		BU_EMP	     	TR_REQ	; 5   ; +
18026e46883SJohn Baldwin 		BU_EMP	TR_RDY	    	; 6   ; -
18126e46883SJohn Baldwin 		BU_EMP	TR_RDY	TR_REQ	; 7   ; +
18226e46883SJohn Baldwin 	RC_RDY 		     		; 8   ; +
18326e46883SJohn Baldwin 	RC_RDY			TR_REQ	; 9   ; +
18426e46883SJohn Baldwin 	RC_RDY		TR_RDY		; 10  ; -
18526e46883SJohn Baldwin 	RC_RDY		TR_RDY	TR_REQ	; 11  ; -
18626e46883SJohn Baldwin 	RC_RDY	BU_EMP			; 12  ; -
18726e46883SJohn Baldwin 	RC_RDY	BU_EMP		TR_REQ	; 13  ; -
18826e46883SJohn Baldwin 	RC_RDY	BU_EMP	TR_RDY		; 14  ; -
18926e46883SJohn Baldwin 	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; -
19026e46883SJohn Baldwin */
19126e46883SJohn Baldwin 
19226e46883SJohn Baldwin #define VALID_DECODER	(2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
19326e46883SJohn Baldwin 
19426e46883SJohn Baldwin int
sbni_probe(struct sbni_softc * sc)19526e46883SJohn Baldwin sbni_probe(struct sbni_softc *sc)
19626e46883SJohn Baldwin {
19726e46883SJohn Baldwin 	u_char csr0;
19826e46883SJohn Baldwin 
19926e46883SJohn Baldwin 	csr0 = sbni_inb(sc, CSR0);
20026e46883SJohn Baldwin 	if (csr0 != 0xff && csr0 != 0x00) {
20126e46883SJohn Baldwin 		csr0 &= ~EN_INT;
20226e46883SJohn Baldwin 		if (csr0 & BU_EMP)
20326e46883SJohn Baldwin 			csr0 |= EN_INT;
20426e46883SJohn Baldwin 
20526e46883SJohn Baldwin 		if (VALID_DECODER & (1 << (csr0 >> 4)))
20626e46883SJohn Baldwin 			return (0);
20726e46883SJohn Baldwin 	}
20826e46883SJohn Baldwin 
20926e46883SJohn Baldwin 	return (ENXIO);
21026e46883SJohn Baldwin }
21126e46883SJohn Baldwin 
21226e46883SJohn Baldwin /*
21326e46883SJohn Baldwin  * Install interface into kernel networking data structures
21426e46883SJohn Baldwin  */
215*aa386085SZhenlei Huang void
sbni_attach(struct sbni_softc * sc,int unit,struct sbni_flags flags)21626e46883SJohn Baldwin sbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
21726e46883SJohn Baldwin {
2183fba06adSJustin Hibbits 	if_t ifp;
21926e46883SJohn Baldwin 	u_char csr0;
2203fba06adSJustin Hibbits 	uint64_t baudrate;
22126e46883SJohn Baldwin 
22226e46883SJohn Baldwin 	ifp = sc->ifp = if_alloc(IFT_ETHER);
22326e46883SJohn Baldwin 	sbni_outb(sc, CSR0, 0);
22426e46883SJohn Baldwin 	set_initial_values(sc, flags);
22526e46883SJohn Baldwin 
22626e46883SJohn Baldwin 	/* Initialize ifnet structure */
2273fba06adSJustin Hibbits 	if_setsoftc(ifp, sc);
22826e46883SJohn Baldwin 	if_initname(ifp, "sbni", unit);
2293fba06adSJustin Hibbits 	if_setinitfn(ifp, sbni_init);
2303fba06adSJustin Hibbits 	if_setstartfn(ifp, sbni_start);
2313fba06adSJustin Hibbits 	if_setioctlfn(ifp, sbni_ioctl);
2323fba06adSJustin Hibbits 	if_setsendqlen(ifp, ifqmaxlen);
23326e46883SJohn Baldwin 
23426e46883SJohn Baldwin 	/* report real baud rate */
23526e46883SJohn Baldwin 	csr0 = sbni_inb(sc, CSR0);
2363fba06adSJustin Hibbits 	baudrate = (csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
23726e46883SJohn Baldwin 
2383fba06adSJustin Hibbits 	if_setbaudrate(ifp, baudrate);
2393fba06adSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
24026e46883SJohn Baldwin 
2413fba06adSJustin Hibbits 	mtx_init(&sc->lock, if_name(ifp), MTX_NETWORK_LOCK, MTX_DEF);
24226e46883SJohn Baldwin 	callout_init_mtx(&sc->wch, &sc->lock, 0);
24326e46883SJohn Baldwin 	ether_ifattach(ifp, sc->enaddr);
24426e46883SJohn Baldwin 	/* device attach does transition from UNCONFIGURED to IDLE state */
24526e46883SJohn Baldwin 
2463fba06adSJustin Hibbits 	if_printf(ifp, "speed %ju, rxl ", (uintmax_t)baudrate);
24726e46883SJohn Baldwin 	if (sc->delta_rxl)
24826e46883SJohn Baldwin 		printf("auto\n");
24926e46883SJohn Baldwin 	else
25026e46883SJohn Baldwin 		printf("%d (fixed)\n", sc->cur_rxl_index);
25126e46883SJohn Baldwin }
25226e46883SJohn Baldwin 
25326e46883SJohn Baldwin void
sbni_detach(struct sbni_softc * sc)25426e46883SJohn Baldwin sbni_detach(struct sbni_softc *sc)
25526e46883SJohn Baldwin {
25626e46883SJohn Baldwin 
25726e46883SJohn Baldwin 	SBNI_LOCK(sc);
25826e46883SJohn Baldwin 	sbni_stop(sc);
25926e46883SJohn Baldwin 	SBNI_UNLOCK(sc);
26026e46883SJohn Baldwin 	callout_drain(&sc->wch);
26126e46883SJohn Baldwin 	ether_ifdetach(sc->ifp);
26226e46883SJohn Baldwin 	if (sc->irq_handle)
26326e46883SJohn Baldwin 		bus_teardown_intr(sc->dev, sc->irq_res, sc->irq_handle);
26426e46883SJohn Baldwin 	mtx_destroy(&sc->lock);
26526e46883SJohn Baldwin 	if_free(sc->ifp);
26626e46883SJohn Baldwin }
26726e46883SJohn Baldwin 
26826e46883SJohn Baldwin void
sbni_release_resources(struct sbni_softc * sc)26926e46883SJohn Baldwin sbni_release_resources(struct sbni_softc *sc)
27026e46883SJohn Baldwin {
27126e46883SJohn Baldwin 
27226e46883SJohn Baldwin 	if (sc->irq_res)
27326e46883SJohn Baldwin 		bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
27426e46883SJohn Baldwin 		    sc->irq_res);
27526e46883SJohn Baldwin 	if (sc->io_res && sc->io_off == 0)
27626e46883SJohn Baldwin 		bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->io_rid,
27726e46883SJohn Baldwin 		    sc->io_res);
27826e46883SJohn Baldwin }
27926e46883SJohn Baldwin 
28026e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
28126e46883SJohn Baldwin 
28226e46883SJohn Baldwin static void
sbni_init(void * xsc)28326e46883SJohn Baldwin sbni_init(void *xsc)
28426e46883SJohn Baldwin {
28526e46883SJohn Baldwin 	struct sbni_softc *sc;
28626e46883SJohn Baldwin 
28726e46883SJohn Baldwin 	sc = (struct sbni_softc *)xsc;
28826e46883SJohn Baldwin 	SBNI_LOCK(sc);
28926e46883SJohn Baldwin 	sbni_init_locked(sc);
29026e46883SJohn Baldwin 	SBNI_UNLOCK(sc);
29126e46883SJohn Baldwin }
29226e46883SJohn Baldwin 
29326e46883SJohn Baldwin static void
sbni_init_locked(struct sbni_softc * sc)29426e46883SJohn Baldwin sbni_init_locked(struct sbni_softc *sc)
29526e46883SJohn Baldwin {
2963fba06adSJustin Hibbits 	if_t ifp;
29726e46883SJohn Baldwin 
29826e46883SJohn Baldwin 	ifp = sc->ifp;
29926e46883SJohn Baldwin 
30026e46883SJohn Baldwin 	/*
30126e46883SJohn Baldwin 	 * kludge to avoid multiple initialization when more than once
30226e46883SJohn Baldwin 	 * protocols configured
30326e46883SJohn Baldwin 	 */
3043fba06adSJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
30526e46883SJohn Baldwin 		return;
30626e46883SJohn Baldwin 
30726e46883SJohn Baldwin 	card_start(sc);
30826e46883SJohn Baldwin 	callout_reset(&sc->wch, hz/SBNI_HZ, sbni_timeout, sc);
30926e46883SJohn Baldwin 
3103fba06adSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
3113fba06adSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
31226e46883SJohn Baldwin 
31326e46883SJohn Baldwin 	/* attempt to start output */
31426e46883SJohn Baldwin 	sbni_start_locked(ifp);
31526e46883SJohn Baldwin }
31626e46883SJohn Baldwin 
31726e46883SJohn Baldwin static void
sbni_start(if_t ifp)3183fba06adSJustin Hibbits sbni_start(if_t ifp)
31926e46883SJohn Baldwin {
3203fba06adSJustin Hibbits 	struct sbni_softc *sc = if_getsoftc(ifp);
32126e46883SJohn Baldwin 
32226e46883SJohn Baldwin 	SBNI_LOCK(sc);
32326e46883SJohn Baldwin 	sbni_start_locked(ifp);
32426e46883SJohn Baldwin 	SBNI_UNLOCK(sc);
32526e46883SJohn Baldwin }
32626e46883SJohn Baldwin 
32726e46883SJohn Baldwin static void
sbni_start_locked(if_t ifp)3283fba06adSJustin Hibbits sbni_start_locked(if_t ifp)
32926e46883SJohn Baldwin {
3303fba06adSJustin Hibbits 	struct sbni_softc *sc = if_getsoftc(ifp);
33126e46883SJohn Baldwin 
33226e46883SJohn Baldwin 	if (sc->tx_frameno == 0)
33326e46883SJohn Baldwin 		prepare_to_send(sc);
33426e46883SJohn Baldwin }
33526e46883SJohn Baldwin 
33626e46883SJohn Baldwin static void
sbni_stop(struct sbni_softc * sc)33726e46883SJohn Baldwin sbni_stop(struct sbni_softc *sc)
33826e46883SJohn Baldwin {
33926e46883SJohn Baldwin 	sbni_outb(sc, CSR0, 0);
34026e46883SJohn Baldwin 	drop_xmit_queue(sc);
34126e46883SJohn Baldwin 
34226e46883SJohn Baldwin 	if (sc->rx_buf_p) {
34326e46883SJohn Baldwin 		m_freem(sc->rx_buf_p);
34426e46883SJohn Baldwin 		sc->rx_buf_p = NULL;
34526e46883SJohn Baldwin 	}
34626e46883SJohn Baldwin 
34726e46883SJohn Baldwin 	callout_stop(&sc->wch);
3483fba06adSJustin Hibbits 	if_setdrvflagbits(sc->ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
34926e46883SJohn Baldwin }
35026e46883SJohn Baldwin 
35126e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
35226e46883SJohn Baldwin 
35326e46883SJohn Baldwin /* interrupt handler */
35426e46883SJohn Baldwin 
35526e46883SJohn Baldwin /*
35626e46883SJohn Baldwin  * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
35726e46883SJohn Baldwin  * be looked as two independent single-channel devices. Every channel seems
35826e46883SJohn Baldwin  * as Ethernet interface but interrupt handler must be common. Really, first
35926e46883SJohn Baldwin  * channel ("master") driver only registers the handler. In it's struct softc
36026e46883SJohn Baldwin  * it has got pointer to "slave" channel's struct softc and handles that's
36126e46883SJohn Baldwin  * interrupts too.
36226e46883SJohn Baldwin  *	softc of successfully attached ISA SBNI boards is linked to list.
36326e46883SJohn Baldwin  * While next board driver is initialized, it scans this list. If one
36426e46883SJohn Baldwin  * has found softc with same irq and ioaddr different by 4 then it assumes
36526e46883SJohn Baldwin  * this board to be "master".
36626e46883SJohn Baldwin  */
36726e46883SJohn Baldwin 
36826e46883SJohn Baldwin void
sbni_intr(void * arg)36926e46883SJohn Baldwin sbni_intr(void *arg)
37026e46883SJohn Baldwin {
37126e46883SJohn Baldwin 	struct sbni_softc *sc;
37226e46883SJohn Baldwin 	int repeat;
37326e46883SJohn Baldwin 
37426e46883SJohn Baldwin 	sc = (struct sbni_softc *)arg;
37526e46883SJohn Baldwin 
37626e46883SJohn Baldwin 	do {
37726e46883SJohn Baldwin 		repeat = 0;
37826e46883SJohn Baldwin 		SBNI_LOCK(sc);
37926e46883SJohn Baldwin 		if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
38026e46883SJohn Baldwin 			handle_channel(sc);
38126e46883SJohn Baldwin 			repeat = 1;
38226e46883SJohn Baldwin 		}
38326e46883SJohn Baldwin 		SBNI_UNLOCK(sc);
38426e46883SJohn Baldwin 		if (sc->slave_sc) {
38526e46883SJohn Baldwin 			/* second channel present */
38626e46883SJohn Baldwin 			SBNI_LOCK(sc->slave_sc);
38726e46883SJohn Baldwin 			if (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY)) {
38826e46883SJohn Baldwin 				handle_channel(sc->slave_sc);
38926e46883SJohn Baldwin 				repeat = 1;
39026e46883SJohn Baldwin 			}
39126e46883SJohn Baldwin 			SBNI_UNLOCK(sc->slave_sc);
39226e46883SJohn Baldwin 		}
39326e46883SJohn Baldwin 	} while (repeat);
39426e46883SJohn Baldwin }
39526e46883SJohn Baldwin 
39626e46883SJohn Baldwin static void
handle_channel(struct sbni_softc * sc)39726e46883SJohn Baldwin handle_channel(struct sbni_softc *sc)
39826e46883SJohn Baldwin {
39926e46883SJohn Baldwin 	int req_ans;
40026e46883SJohn Baldwin 	u_char csr0;
40126e46883SJohn Baldwin 
40226e46883SJohn Baldwin 	sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
40326e46883SJohn Baldwin 
40426e46883SJohn Baldwin 	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
40526e46883SJohn Baldwin 	for (;;) {
40626e46883SJohn Baldwin 		csr0 = sbni_inb(sc, CSR0);
40726e46883SJohn Baldwin 		if ((csr0 & (RC_RDY | TR_RDY)) == 0)
40826e46883SJohn Baldwin 			break;
40926e46883SJohn Baldwin 
41026e46883SJohn Baldwin 		req_ans = !(sc->state & FL_PREV_OK);
41126e46883SJohn Baldwin 
41226e46883SJohn Baldwin 		if (csr0 & RC_RDY)
41326e46883SJohn Baldwin 			req_ans = recv_frame(sc);
41426e46883SJohn Baldwin 
41526e46883SJohn Baldwin 		/*
41626e46883SJohn Baldwin 		 * TR_RDY always equals 1 here because we have owned the marker,
41726e46883SJohn Baldwin 		 * and we set TR_REQ when disabled interrupts
41826e46883SJohn Baldwin 		 */
41926e46883SJohn Baldwin 		csr0 = sbni_inb(sc, CSR0);
42026e46883SJohn Baldwin 		if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
42126e46883SJohn Baldwin 			if_printf(sc->ifp, "internal error!\n");
42226e46883SJohn Baldwin 
42326e46883SJohn Baldwin 		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
42426e46883SJohn Baldwin 		if (req_ans || sc->tx_frameno != 0)
42526e46883SJohn Baldwin 			send_frame(sc);
42626e46883SJohn Baldwin 		else {
42726e46883SJohn Baldwin 			/* send the marker without any data */
42826e46883SJohn Baldwin 			sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
42926e46883SJohn Baldwin 		}
43026e46883SJohn Baldwin 	}
43126e46883SJohn Baldwin 
43226e46883SJohn Baldwin 	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
43326e46883SJohn Baldwin }
43426e46883SJohn Baldwin 
43526e46883SJohn Baldwin /*
43626e46883SJohn Baldwin  * Routine returns 1 if it need to acknoweledge received frame.
43726e46883SJohn Baldwin  * Empty frame received without errors won't be acknoweledged.
43826e46883SJohn Baldwin  */
43926e46883SJohn Baldwin 
44026e46883SJohn Baldwin static int
recv_frame(struct sbni_softc * sc)44126e46883SJohn Baldwin recv_frame(struct sbni_softc *sc)
44226e46883SJohn Baldwin {
44326e46883SJohn Baldwin 	u_int32_t crc;
44426e46883SJohn Baldwin 	u_int framelen, frameno, ack;
44526e46883SJohn Baldwin 	u_int is_first, frame_ok;
44626e46883SJohn Baldwin 
44726e46883SJohn Baldwin 	crc = CRC32_INITIAL;
44826e46883SJohn Baldwin 	if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
44926e46883SJohn Baldwin 		frame_ok = framelen > 4 ?
45026e46883SJohn Baldwin 		    upload_data(sc, framelen, frameno, is_first, crc) :
45126e46883SJohn Baldwin 		    skip_tail(sc, framelen, crc);
45226e46883SJohn Baldwin 		if (frame_ok)
45326e46883SJohn Baldwin 			interpret_ack(sc, ack);
45426e46883SJohn Baldwin 	} else {
45526e46883SJohn Baldwin 		framelen = 0;
45626e46883SJohn Baldwin 		frame_ok = 0;
45726e46883SJohn Baldwin 	}
45826e46883SJohn Baldwin 
45926e46883SJohn Baldwin 	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
46026e46883SJohn Baldwin 	if (frame_ok) {
46126e46883SJohn Baldwin 		sc->state |= FL_PREV_OK;
46226e46883SJohn Baldwin 		if (framelen > 4)
46326e46883SJohn Baldwin 			sc->in_stats.all_rx_number++;
46426e46883SJohn Baldwin 	} else {
46526e46883SJohn Baldwin 		sc->state &= ~FL_PREV_OK;
46626e46883SJohn Baldwin 		change_level(sc);
46726e46883SJohn Baldwin 		sc->in_stats.all_rx_number++;
46826e46883SJohn Baldwin 		sc->in_stats.bad_rx_number++;
46926e46883SJohn Baldwin 	}
47026e46883SJohn Baldwin 
47126e46883SJohn Baldwin 	return (!frame_ok || framelen > 4);
47226e46883SJohn Baldwin }
47326e46883SJohn Baldwin 
47426e46883SJohn Baldwin static void
send_frame(struct sbni_softc * sc)47526e46883SJohn Baldwin send_frame(struct sbni_softc *sc)
47626e46883SJohn Baldwin {
47726e46883SJohn Baldwin 	u_int32_t crc;
47826e46883SJohn Baldwin 	u_char csr0;
47926e46883SJohn Baldwin 
48026e46883SJohn Baldwin 	crc = CRC32_INITIAL;
48126e46883SJohn Baldwin 	if (sc->state & FL_NEED_RESEND) {
48226e46883SJohn Baldwin 		/* if frame was sended but not ACK'ed - resend it */
48326e46883SJohn Baldwin 		if (sc->trans_errors) {
48426e46883SJohn Baldwin 			sc->trans_errors--;
48526e46883SJohn Baldwin 			if (sc->framelen != 0)
48626e46883SJohn Baldwin 				sc->in_stats.resend_tx_number++;
48726e46883SJohn Baldwin 		} else {
48826e46883SJohn Baldwin 			/* cannot xmit with many attempts */
48926e46883SJohn Baldwin 			drop_xmit_queue(sc);
49026e46883SJohn Baldwin 			goto do_send;
49126e46883SJohn Baldwin 		}
49226e46883SJohn Baldwin 	} else
49326e46883SJohn Baldwin 		sc->trans_errors = TR_ERROR_COUNT;
49426e46883SJohn Baldwin 
49526e46883SJohn Baldwin 	send_frame_header(sc, &crc);
49626e46883SJohn Baldwin 	sc->state |= FL_NEED_RESEND;
49726e46883SJohn Baldwin 	/*
49826e46883SJohn Baldwin 	 * FL_NEED_RESEND will be cleared after ACK, but if empty
49926e46883SJohn Baldwin 	 * frame sended then in prepare_to_send next frame
50026e46883SJohn Baldwin 	 */
50126e46883SJohn Baldwin 
50226e46883SJohn Baldwin 	if (sc->framelen) {
50326e46883SJohn Baldwin 		download_data(sc, &crc);
50426e46883SJohn Baldwin 		sc->in_stats.all_tx_number++;
50526e46883SJohn Baldwin 		sc->state |= FL_WAIT_ACK;
50626e46883SJohn Baldwin 	}
50726e46883SJohn Baldwin 
50826e46883SJohn Baldwin 	sbni_outsb(sc, (u_char *)&crc, sizeof crc);
50926e46883SJohn Baldwin 
51026e46883SJohn Baldwin do_send:
51126e46883SJohn Baldwin 	csr0 = sbni_inb(sc, CSR0);
51226e46883SJohn Baldwin 	sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
51326e46883SJohn Baldwin 
51426e46883SJohn Baldwin 	if (sc->tx_frameno) {
51526e46883SJohn Baldwin 		/* next frame exists - request to send */
51626e46883SJohn Baldwin 		sbni_outb(sc, CSR0, csr0 | TR_REQ);
51726e46883SJohn Baldwin 	}
51826e46883SJohn Baldwin }
51926e46883SJohn Baldwin 
52026e46883SJohn Baldwin static void
download_data(struct sbni_softc * sc,u_int32_t * crc_p)52126e46883SJohn Baldwin download_data(struct sbni_softc *sc, u_int32_t *crc_p)
52226e46883SJohn Baldwin {
52326e46883SJohn Baldwin 	struct mbuf *m;
52426e46883SJohn Baldwin 	caddr_t	data_p;
52526e46883SJohn Baldwin 	u_int data_len, pos, slice;
52626e46883SJohn Baldwin 
52726e46883SJohn Baldwin 	data_p = NULL;		/* initialized to avoid warn */
52826e46883SJohn Baldwin 	pos = 0;
52926e46883SJohn Baldwin 
53026e46883SJohn Baldwin 	for (m = sc->tx_buf_p;  m != NULL && pos < sc->pktlen;  m = m->m_next) {
53126e46883SJohn Baldwin 		if (pos + m->m_len > sc->outpos) {
53226e46883SJohn Baldwin 			data_len = m->m_len - (sc->outpos - pos);
53326e46883SJohn Baldwin 			data_p = mtod(m, caddr_t) + (sc->outpos - pos);
53426e46883SJohn Baldwin 
53526e46883SJohn Baldwin 			goto do_copy;
53626e46883SJohn Baldwin 		} else
53726e46883SJohn Baldwin 			pos += m->m_len;
53826e46883SJohn Baldwin 	}
53926e46883SJohn Baldwin 
54026e46883SJohn Baldwin 	data_len = 0;
54126e46883SJohn Baldwin 
54226e46883SJohn Baldwin do_copy:
54326e46883SJohn Baldwin 	pos = 0;
54426e46883SJohn Baldwin 	do {
54526e46883SJohn Baldwin 		if (data_len) {
54626e46883SJohn Baldwin 			slice = min(data_len, sc->framelen - pos);
54726e46883SJohn Baldwin 			sbni_outsb(sc, data_p, slice);
54826e46883SJohn Baldwin 			*crc_p = calc_crc32(*crc_p, data_p, slice);
54926e46883SJohn Baldwin 
55026e46883SJohn Baldwin 			pos += slice;
55126e46883SJohn Baldwin 			if (data_len -= slice)
55226e46883SJohn Baldwin 				data_p += slice;
55326e46883SJohn Baldwin 			else {
55426e46883SJohn Baldwin 				do {
55526e46883SJohn Baldwin 					m = m->m_next;
55626e46883SJohn Baldwin 				} while (m != NULL && m->m_len == 0);
55726e46883SJohn Baldwin 
55826e46883SJohn Baldwin 				if (m) {
55926e46883SJohn Baldwin 					data_len = m->m_len;
56026e46883SJohn Baldwin 					data_p = mtod(m, caddr_t);
56126e46883SJohn Baldwin 				}
56226e46883SJohn Baldwin 			}
56326e46883SJohn Baldwin 		} else {
56426e46883SJohn Baldwin 			/* frame too short - zero padding */
56526e46883SJohn Baldwin 
56626e46883SJohn Baldwin 			pos = sc->framelen - pos;
56726e46883SJohn Baldwin 			while (pos--) {
56826e46883SJohn Baldwin 				sbni_outb(sc, DAT, 0);
56926e46883SJohn Baldwin 				*crc_p = CRC32(0, *crc_p);
57026e46883SJohn Baldwin 			}
57126e46883SJohn Baldwin 			return;
57226e46883SJohn Baldwin 		}
57326e46883SJohn Baldwin 	} while (pos < sc->framelen);
57426e46883SJohn Baldwin }
57526e46883SJohn Baldwin 
57626e46883SJohn Baldwin static int
upload_data(struct sbni_softc * sc,u_int framelen,u_int frameno,u_int is_first,u_int32_t crc)57726e46883SJohn Baldwin upload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
57826e46883SJohn Baldwin 	    u_int is_first, u_int32_t crc)
57926e46883SJohn Baldwin {
58026e46883SJohn Baldwin 	int frame_ok;
58126e46883SJohn Baldwin 
58226e46883SJohn Baldwin 	if (is_first) {
58326e46883SJohn Baldwin 		sc->wait_frameno = frameno;
58426e46883SJohn Baldwin 		sc->inppos = 0;
58526e46883SJohn Baldwin 	}
58626e46883SJohn Baldwin 
58726e46883SJohn Baldwin 	if (sc->wait_frameno == frameno) {
58826e46883SJohn Baldwin 		if (sc->inppos + framelen  <=  ETHER_MAX_LEN) {
58926e46883SJohn Baldwin 			frame_ok = append_frame_to_pkt(sc, framelen, crc);
59026e46883SJohn Baldwin 
59126e46883SJohn Baldwin 		/*
59226e46883SJohn Baldwin 		 * if CRC is right but framelen incorrect then transmitter
593453130d9SPedro F. Giffuni 		 * error was occurred... drop entire packet
59426e46883SJohn Baldwin 		 */
59526e46883SJohn Baldwin 		} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
59626e46883SJohn Baldwin 			sc->wait_frameno = 0;
59726e46883SJohn Baldwin 			sc->inppos = 0;
598c8dfaf38SGleb Smirnoff 			if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
59926e46883SJohn Baldwin 			/* now skip all frames until is_first != 0 */
60026e46883SJohn Baldwin 		}
60126e46883SJohn Baldwin 	} else
60226e46883SJohn Baldwin 		frame_ok = skip_tail(sc, framelen, crc);
60326e46883SJohn Baldwin 
60426e46883SJohn Baldwin 	if (is_first && !frame_ok) {
60526e46883SJohn Baldwin 		/*
60626e46883SJohn Baldwin 		 * Frame has been violated, but we have stored
60726e46883SJohn Baldwin 		 * is_first already... Drop entire packet.
60826e46883SJohn Baldwin 		 */
60926e46883SJohn Baldwin 		sc->wait_frameno = 0;
610c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
61126e46883SJohn Baldwin 	}
61226e46883SJohn Baldwin 
61326e46883SJohn Baldwin 	return (frame_ok);
61426e46883SJohn Baldwin }
61526e46883SJohn Baldwin 
61626e46883SJohn Baldwin static __inline void	send_complete(struct sbni_softc *);
61726e46883SJohn Baldwin 
61826e46883SJohn Baldwin static __inline void
send_complete(struct sbni_softc * sc)61926e46883SJohn Baldwin send_complete(struct sbni_softc *sc)
62026e46883SJohn Baldwin {
62126e46883SJohn Baldwin 	m_freem(sc->tx_buf_p);
62226e46883SJohn Baldwin 	sc->tx_buf_p = NULL;
623c8dfaf38SGleb Smirnoff 	if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1);
62426e46883SJohn Baldwin }
62526e46883SJohn Baldwin 
62626e46883SJohn Baldwin static void
interpret_ack(struct sbni_softc * sc,u_int ack)62726e46883SJohn Baldwin interpret_ack(struct sbni_softc *sc, u_int ack)
62826e46883SJohn Baldwin {
62926e46883SJohn Baldwin 	if (ack == FRAME_SENT_OK) {
63026e46883SJohn Baldwin 		sc->state &= ~FL_NEED_RESEND;
63126e46883SJohn Baldwin 
63226e46883SJohn Baldwin 		if (sc->state & FL_WAIT_ACK) {
63326e46883SJohn Baldwin 			sc->outpos += sc->framelen;
63426e46883SJohn Baldwin 
63526e46883SJohn Baldwin 			if (--sc->tx_frameno) {
63626e46883SJohn Baldwin 				sc->framelen = min(
63726e46883SJohn Baldwin 				    sc->maxframe, sc->pktlen - sc->outpos);
63826e46883SJohn Baldwin 			} else {
63926e46883SJohn Baldwin 				send_complete(sc);
64026e46883SJohn Baldwin 				prepare_to_send(sc);
64126e46883SJohn Baldwin 			}
64226e46883SJohn Baldwin 		}
64326e46883SJohn Baldwin 	}
64426e46883SJohn Baldwin 
64526e46883SJohn Baldwin 	sc->state &= ~FL_WAIT_ACK;
64626e46883SJohn Baldwin }
64726e46883SJohn Baldwin 
64826e46883SJohn Baldwin /*
64926e46883SJohn Baldwin  * Glue received frame with previous fragments of packet.
65026e46883SJohn Baldwin  * Indicate packet when last frame would be accepted.
65126e46883SJohn Baldwin  */
65226e46883SJohn Baldwin 
65326e46883SJohn Baldwin static int
append_frame_to_pkt(struct sbni_softc * sc,u_int framelen,u_int32_t crc)65426e46883SJohn Baldwin append_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
65526e46883SJohn Baldwin {
65626e46883SJohn Baldwin 	caddr_t p;
65726e46883SJohn Baldwin 
65826e46883SJohn Baldwin 	if (sc->inppos + framelen > ETHER_MAX_LEN)
65926e46883SJohn Baldwin 		return (0);
66026e46883SJohn Baldwin 
66126e46883SJohn Baldwin 	if (!sc->rx_buf_p && !get_rx_buf(sc))
66226e46883SJohn Baldwin 		return (0);
66326e46883SJohn Baldwin 
66426e46883SJohn Baldwin 	p = sc->rx_buf_p->m_data + sc->inppos;
66526e46883SJohn Baldwin 	sbni_insb(sc, p, framelen);
66626e46883SJohn Baldwin 	if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
66726e46883SJohn Baldwin 		return (0);
66826e46883SJohn Baldwin 
66926e46883SJohn Baldwin 	sc->inppos += framelen - 4;
67026e46883SJohn Baldwin 	if (--sc->wait_frameno == 0) {		/* last frame received */
67126e46883SJohn Baldwin 		indicate_pkt(sc);
672c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1);
67326e46883SJohn Baldwin 	}
67426e46883SJohn Baldwin 
67526e46883SJohn Baldwin 	return (1);
67626e46883SJohn Baldwin }
67726e46883SJohn Baldwin 
67826e46883SJohn Baldwin /*
67926e46883SJohn Baldwin  * Prepare to start output on adapter. Current priority must be set to splimp
68026e46883SJohn Baldwin  * before this routine is called.
68126e46883SJohn Baldwin  * Transmitter will be actually activated when marker has been accepted.
68226e46883SJohn Baldwin  */
68326e46883SJohn Baldwin 
68426e46883SJohn Baldwin static void
prepare_to_send(struct sbni_softc * sc)68526e46883SJohn Baldwin prepare_to_send(struct sbni_softc *sc)
68626e46883SJohn Baldwin {
68726e46883SJohn Baldwin 	struct mbuf *m;
68826e46883SJohn Baldwin 	u_int len;
68926e46883SJohn Baldwin 
69026e46883SJohn Baldwin 	/* sc->tx_buf_p == NULL here! */
69126e46883SJohn Baldwin 	if (sc->tx_buf_p)
69226e46883SJohn Baldwin 		printf("sbni: memory leak!\n");
69326e46883SJohn Baldwin 
69426e46883SJohn Baldwin 	sc->outpos = 0;
69526e46883SJohn Baldwin 	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
69626e46883SJohn Baldwin 
69726e46883SJohn Baldwin 	for (;;) {
6983fba06adSJustin Hibbits 		sc->tx_buf_p = if_dequeue(sc->ifp);
69926e46883SJohn Baldwin 		if (!sc->tx_buf_p) {
70026e46883SJohn Baldwin 			/* nothing to transmit... */
70126e46883SJohn Baldwin 			sc->pktlen     = 0;
70226e46883SJohn Baldwin 			sc->tx_frameno = 0;
70326e46883SJohn Baldwin 			sc->framelen   = 0;
7043fba06adSJustin Hibbits 			if_setdrvflagbits(sc->ifp, 0, IFF_DRV_OACTIVE);
70526e46883SJohn Baldwin 			return;
70626e46883SJohn Baldwin 		}
70726e46883SJohn Baldwin 
70826e46883SJohn Baldwin 		for (len = 0, m = sc->tx_buf_p;  m;  m = m->m_next)
70926e46883SJohn Baldwin 			len += m->m_len;
71026e46883SJohn Baldwin 
71126e46883SJohn Baldwin 		if (len != 0)
71226e46883SJohn Baldwin 			break;
71326e46883SJohn Baldwin 		m_freem(sc->tx_buf_p);
71426e46883SJohn Baldwin 	}
71526e46883SJohn Baldwin 
71626e46883SJohn Baldwin 	if (len < SBNI_MIN_LEN)
71726e46883SJohn Baldwin 		len = SBNI_MIN_LEN;
71826e46883SJohn Baldwin 
71926e46883SJohn Baldwin 	sc->pktlen	= len;
720057b4402SPedro F. Giffuni 	sc->tx_frameno	= howmany(len, sc->maxframe);
72126e46883SJohn Baldwin 	sc->framelen	= min(len, sc->maxframe);
72226e46883SJohn Baldwin 
72326e46883SJohn Baldwin 	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
7243fba06adSJustin Hibbits 	if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0);
72526e46883SJohn Baldwin 	BPF_MTAP(sc->ifp, sc->tx_buf_p);
72626e46883SJohn Baldwin }
72726e46883SJohn Baldwin 
72826e46883SJohn Baldwin static void
drop_xmit_queue(struct sbni_softc * sc)72926e46883SJohn Baldwin drop_xmit_queue(struct sbni_softc *sc)
73026e46883SJohn Baldwin {
73126e46883SJohn Baldwin 	struct mbuf *m;
73226e46883SJohn Baldwin 
73326e46883SJohn Baldwin 	if (sc->tx_buf_p) {
73426e46883SJohn Baldwin 		m_freem(sc->tx_buf_p);
73526e46883SJohn Baldwin 		sc->tx_buf_p = NULL;
736c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
73726e46883SJohn Baldwin 	}
73826e46883SJohn Baldwin 
73926e46883SJohn Baldwin 	for (;;) {
7403fba06adSJustin Hibbits 		m = if_dequeue(sc->ifp);
74126e46883SJohn Baldwin 		if (m == NULL)
74226e46883SJohn Baldwin 			break;
74326e46883SJohn Baldwin 		m_freem(m);
744c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1);
74526e46883SJohn Baldwin 	}
74626e46883SJohn Baldwin 
74726e46883SJohn Baldwin 	sc->tx_frameno	= 0;
74826e46883SJohn Baldwin 	sc->framelen	= 0;
74926e46883SJohn Baldwin 	sc->outpos	= 0;
75026e46883SJohn Baldwin 	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
7513fba06adSJustin Hibbits 	if_setdrvflagbits(sc->ifp, 0, IFF_DRV_OACTIVE);
75226e46883SJohn Baldwin }
75326e46883SJohn Baldwin 
75426e46883SJohn Baldwin static void
send_frame_header(struct sbni_softc * sc,u_int32_t * crc_p)75526e46883SJohn Baldwin send_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
75626e46883SJohn Baldwin {
75726e46883SJohn Baldwin 	u_int32_t crc;
75826e46883SJohn Baldwin 	u_int len_field;
75926e46883SJohn Baldwin 	u_char value;
76026e46883SJohn Baldwin 
76126e46883SJohn Baldwin 	crc = *crc_p;
76226e46883SJohn Baldwin 	len_field = sc->framelen + 6;	/* CRC + frameno + reserved */
76326e46883SJohn Baldwin 
76426e46883SJohn Baldwin 	if (sc->state & FL_NEED_RESEND)
76526e46883SJohn Baldwin 		len_field |= FRAME_RETRY;	/* non-first attempt... */
76626e46883SJohn Baldwin 
76726e46883SJohn Baldwin 	if (sc->outpos == 0)
76826e46883SJohn Baldwin 		len_field |= FRAME_FIRST;
76926e46883SJohn Baldwin 
77026e46883SJohn Baldwin 	len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
77126e46883SJohn Baldwin 	sbni_outb(sc, DAT, SBNI_SIG);
77226e46883SJohn Baldwin 
77326e46883SJohn Baldwin 	value = (u_char)len_field;
77426e46883SJohn Baldwin 	sbni_outb(sc, DAT, value);
77526e46883SJohn Baldwin 	crc = CRC32(value, crc);
77626e46883SJohn Baldwin 	value = (u_char)(len_field >> 8);
77726e46883SJohn Baldwin 	sbni_outb(sc, DAT, value);
77826e46883SJohn Baldwin 	crc = CRC32(value, crc);
77926e46883SJohn Baldwin 
78026e46883SJohn Baldwin 	sbni_outb(sc, DAT, sc->tx_frameno);
78126e46883SJohn Baldwin 	crc = CRC32(sc->tx_frameno, crc);
78226e46883SJohn Baldwin 	sbni_outb(sc, DAT, 0);
78326e46883SJohn Baldwin 	crc = CRC32(0, crc);
78426e46883SJohn Baldwin 	*crc_p = crc;
78526e46883SJohn Baldwin }
78626e46883SJohn Baldwin 
78726e46883SJohn Baldwin /*
78826e46883SJohn Baldwin  * if frame tail not needed (incorrect number or received twice),
78926e46883SJohn Baldwin  * it won't store, but CRC will be calculated
79026e46883SJohn Baldwin  */
79126e46883SJohn Baldwin 
79226e46883SJohn Baldwin static int
skip_tail(struct sbni_softc * sc,u_int tail_len,u_int32_t crc)79326e46883SJohn Baldwin skip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
79426e46883SJohn Baldwin {
79526e46883SJohn Baldwin 	while (tail_len--)
79626e46883SJohn Baldwin 		crc = CRC32(sbni_inb(sc, DAT), crc);
79726e46883SJohn Baldwin 
79826e46883SJohn Baldwin 	return (crc == CRC32_REMAINDER);
79926e46883SJohn Baldwin }
80026e46883SJohn Baldwin 
80126e46883SJohn Baldwin 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)80226e46883SJohn Baldwin check_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
80326e46883SJohn Baldwin 	   u_int *ack, u_int *is_first, u_int32_t *crc_p)
80426e46883SJohn Baldwin {
80526e46883SJohn Baldwin 	u_int32_t crc;
80626e46883SJohn Baldwin 	u_char value;
80726e46883SJohn Baldwin 
80826e46883SJohn Baldwin 	crc = *crc_p;
80926e46883SJohn Baldwin 	if (sbni_inb(sc, DAT) != SBNI_SIG)
81026e46883SJohn Baldwin 		return (0);
81126e46883SJohn Baldwin 
81226e46883SJohn Baldwin 	value = sbni_inb(sc, DAT);
81326e46883SJohn Baldwin 	*framelen = (u_int)value;
81426e46883SJohn Baldwin 	crc = CRC32(value, crc);
81526e46883SJohn Baldwin 	value = sbni_inb(sc, DAT);
81626e46883SJohn Baldwin 	*framelen |= ((u_int)value) << 8;
81726e46883SJohn Baldwin 	crc = CRC32(value, crc);
81826e46883SJohn Baldwin 
81926e46883SJohn Baldwin 	*ack = *framelen & FRAME_ACK_MASK;
82026e46883SJohn Baldwin 	*is_first = (*framelen & FRAME_FIRST) != 0;
82126e46883SJohn Baldwin 
82226e46883SJohn Baldwin 	if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
82326e46883SJohn Baldwin 		return (0);
82426e46883SJohn Baldwin 
82526e46883SJohn Baldwin 	value = sbni_inb(sc, DAT);
82626e46883SJohn Baldwin 	*frameno = (u_int)value;
82726e46883SJohn Baldwin 	crc = CRC32(value, crc);
82826e46883SJohn Baldwin 
82926e46883SJohn Baldwin 	crc = CRC32(sbni_inb(sc, DAT), crc);		/* reserved byte */
83026e46883SJohn Baldwin 	*framelen -= 2;
83126e46883SJohn Baldwin 
83226e46883SJohn Baldwin 	*crc_p = crc;
83326e46883SJohn Baldwin 	return (1);
83426e46883SJohn Baldwin }
83526e46883SJohn Baldwin 
83626e46883SJohn Baldwin static int
get_rx_buf(struct sbni_softc * sc)83726e46883SJohn Baldwin get_rx_buf(struct sbni_softc *sc)
83826e46883SJohn Baldwin {
83926e46883SJohn Baldwin 	struct mbuf *m;
84026e46883SJohn Baldwin 
841c6499eccSGleb Smirnoff 	MGETHDR(m, M_NOWAIT, MT_DATA);
84226e46883SJohn Baldwin 	if (m == NULL) {
84326e46883SJohn Baldwin 		if_printf(sc->ifp, "cannot allocate header mbuf\n");
84426e46883SJohn Baldwin 		return (0);
84526e46883SJohn Baldwin 	}
84626e46883SJohn Baldwin 
84726e46883SJohn Baldwin 	/*
84826e46883SJohn Baldwin 	 * We always put the received packet in a single buffer -
84926e46883SJohn Baldwin 	 * either with just an mbuf header or in a cluster attached
85026e46883SJohn Baldwin 	 * to the header. The +2 is to compensate for the alignment
85126e46883SJohn Baldwin 	 * fixup below.
85226e46883SJohn Baldwin 	 */
85326e46883SJohn Baldwin 	if (ETHER_MAX_LEN + 2 > MHLEN) {
85426e46883SJohn Baldwin 		/* Attach an mbuf cluster */
8552a8c860fSRobert Watson 		if (!(MCLGET(m, M_NOWAIT))) {
85626e46883SJohn Baldwin 			m_freem(m);
85726e46883SJohn Baldwin 			return (0);
85826e46883SJohn Baldwin 		}
85926e46883SJohn Baldwin 	}
86026e46883SJohn Baldwin 	m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
86126e46883SJohn Baldwin 
86226e46883SJohn Baldwin 	/*
86326e46883SJohn Baldwin 	 * The +2 is to longword align the start of the real packet.
86426e46883SJohn Baldwin 	 * (sizeof ether_header == 14)
86526e46883SJohn Baldwin 	 * This is important for NFS.
86626e46883SJohn Baldwin 	 */
86726e46883SJohn Baldwin 	m_adj(m, 2);
86826e46883SJohn Baldwin 	sc->rx_buf_p = m;
86926e46883SJohn Baldwin 	return (1);
87026e46883SJohn Baldwin }
87126e46883SJohn Baldwin 
87226e46883SJohn Baldwin static void
indicate_pkt(struct sbni_softc * sc)87326e46883SJohn Baldwin indicate_pkt(struct sbni_softc *sc)
87426e46883SJohn Baldwin {
8753fba06adSJustin Hibbits 	if_t ifp = sc->ifp;
87626e46883SJohn Baldwin 	struct mbuf *m;
87726e46883SJohn Baldwin 
87826e46883SJohn Baldwin 	m = sc->rx_buf_p;
87926e46883SJohn Baldwin 	m->m_pkthdr.rcvif = ifp;
88026e46883SJohn Baldwin 	m->m_pkthdr.len   = m->m_len = sc->inppos;
88126e46883SJohn Baldwin 	sc->rx_buf_p = NULL;
88226e46883SJohn Baldwin 
88326e46883SJohn Baldwin 	SBNI_UNLOCK(sc);
8843fba06adSJustin Hibbits 	if_input(ifp, m);
88526e46883SJohn Baldwin 	SBNI_LOCK(sc);
88626e46883SJohn Baldwin }
88726e46883SJohn Baldwin 
88826e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
88926e46883SJohn Baldwin 
89026e46883SJohn Baldwin /*
89126e46883SJohn Baldwin  * Routine checks periodically wire activity and regenerates marker if
89226e46883SJohn Baldwin  * connect was inactive for a long time.
89326e46883SJohn Baldwin  */
89426e46883SJohn Baldwin 
89526e46883SJohn Baldwin static void
sbni_timeout(void * xsc)89626e46883SJohn Baldwin sbni_timeout(void *xsc)
89726e46883SJohn Baldwin {
89826e46883SJohn Baldwin 	struct sbni_softc *sc;
89926e46883SJohn Baldwin 	u_char csr0;
90026e46883SJohn Baldwin 
90126e46883SJohn Baldwin 	sc = (struct sbni_softc *)xsc;
90226e46883SJohn Baldwin 	SBNI_ASSERT_LOCKED(sc);
90326e46883SJohn Baldwin 
90426e46883SJohn Baldwin 	csr0 = sbni_inb(sc, CSR0);
90526e46883SJohn Baldwin 	if (csr0 & RC_CHK) {
90626e46883SJohn Baldwin 		if (sc->timer_ticks) {
90726e46883SJohn Baldwin 			if (csr0 & (RC_RDY | BU_EMP))
90826e46883SJohn Baldwin 				/* receiving not active */
90926e46883SJohn Baldwin 				sc->timer_ticks--;
91026e46883SJohn Baldwin 		} else {
91126e46883SJohn Baldwin 			sc->in_stats.timeout_number++;
91226e46883SJohn Baldwin 			if (sc->delta_rxl)
91326e46883SJohn Baldwin 				timeout_change_level(sc);
91426e46883SJohn Baldwin 
91526e46883SJohn Baldwin 			sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
91626e46883SJohn Baldwin 			csr0 = sbni_inb(sc, CSR0);
91726e46883SJohn Baldwin 		}
91826e46883SJohn Baldwin 	}
91926e46883SJohn Baldwin 
92026e46883SJohn Baldwin 	sbni_outb(sc, CSR0, csr0 | RC_CHK);
92126e46883SJohn Baldwin 	callout_reset(&sc->wch, hz/SBNI_HZ, sbni_timeout, sc);
92226e46883SJohn Baldwin }
92326e46883SJohn Baldwin 
92426e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
92526e46883SJohn Baldwin 
92626e46883SJohn Baldwin static void
card_start(struct sbni_softc * sc)92726e46883SJohn Baldwin card_start(struct sbni_softc *sc)
92826e46883SJohn Baldwin {
92926e46883SJohn Baldwin 	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
93026e46883SJohn Baldwin 	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
93126e46883SJohn Baldwin 	sc->state |= FL_PREV_OK;
93226e46883SJohn Baldwin 
93326e46883SJohn Baldwin 	sc->inppos = 0;
93426e46883SJohn Baldwin 	sc->wait_frameno = 0;
93526e46883SJohn Baldwin 
93626e46883SJohn Baldwin 	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
93726e46883SJohn Baldwin 	sbni_outb(sc, CSR0, EN_INT);
93826e46883SJohn Baldwin }
93926e46883SJohn Baldwin 
94026e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
94126e46883SJohn Baldwin 
94226e46883SJohn Baldwin static u_char rxl_tab[] = {
94326e46883SJohn Baldwin 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
94426e46883SJohn Baldwin 	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
94526e46883SJohn Baldwin };
94626e46883SJohn Baldwin 
94726e46883SJohn Baldwin #define SIZE_OF_TIMEOUT_RXL_TAB 4
94826e46883SJohn Baldwin static u_char timeout_rxl_tab[] = {
94926e46883SJohn Baldwin 	0x03, 0x05, 0x08, 0x0b
95026e46883SJohn Baldwin };
95126e46883SJohn Baldwin 
95226e46883SJohn Baldwin static void
set_initial_values(struct sbni_softc * sc,struct sbni_flags flags)95326e46883SJohn Baldwin set_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
95426e46883SJohn Baldwin {
95526e46883SJohn Baldwin 	if (flags.fixed_rxl) {
95626e46883SJohn Baldwin 		sc->delta_rxl = 0; /* disable receive level autodetection */
95726e46883SJohn Baldwin 		sc->cur_rxl_index = flags.rxl;
95826e46883SJohn Baldwin 	} else {
95926e46883SJohn Baldwin 		sc->delta_rxl = DEF_RXL_DELTA;
96026e46883SJohn Baldwin 		sc->cur_rxl_index = DEF_RXL;
96126e46883SJohn Baldwin 	}
96226e46883SJohn Baldwin 
96326e46883SJohn Baldwin 	sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
96426e46883SJohn Baldwin 	sc->csr1.rxl  = rxl_tab[sc->cur_rxl_index];
96526e46883SJohn Baldwin 	sc->maxframe  = DEFAULT_FRAME_LEN;
96626e46883SJohn Baldwin 
96726e46883SJohn Baldwin 	/*
96826e46883SJohn Baldwin 	 * generate Ethernet address (0x00ff01xxxxxx)
96926e46883SJohn Baldwin 	 */
97026e46883SJohn Baldwin 	*(u_int16_t *) sc->enaddr = htons(0x00ff);
97126e46883SJohn Baldwin 	if (flags.mac_addr) {
97226e46883SJohn Baldwin 		*(u_int32_t *) (sc->enaddr + 2) =
97326e46883SJohn Baldwin 		    htonl(flags.mac_addr | 0x01000000);
97426e46883SJohn Baldwin 	} else {
97526e46883SJohn Baldwin 		*(u_char *) (sc->enaddr + 2) = 0x01;
97626e46883SJohn Baldwin 		read_random(sc->enaddr + 3, 3);
97726e46883SJohn Baldwin 	}
97826e46883SJohn Baldwin }
97926e46883SJohn Baldwin 
98026e46883SJohn Baldwin #ifdef SBNI_DUAL_COMPOUND
98126e46883SJohn Baldwin void
sbni_add(struct sbni_softc * sc)98226e46883SJohn Baldwin sbni_add(struct sbni_softc *sc)
98326e46883SJohn Baldwin {
98426e46883SJohn Baldwin 
98526e46883SJohn Baldwin 	mtx_lock(&headlist_lock);
98626e46883SJohn Baldwin 	sc->link = sbni_headlist;
98726e46883SJohn Baldwin 	sbni_headlist = sc;
98826e46883SJohn Baldwin 	mtx_unlock(&headlist_lock);
98926e46883SJohn Baldwin }
99026e46883SJohn Baldwin 
99126e46883SJohn Baldwin struct sbni_softc *
connect_to_master(struct sbni_softc * sc)99226e46883SJohn Baldwin connect_to_master(struct sbni_softc *sc)
99326e46883SJohn Baldwin {
99426e46883SJohn Baldwin 	struct sbni_softc *p, *p_prev;
99526e46883SJohn Baldwin 
99626e46883SJohn Baldwin 	mtx_lock(&headlist_lock);
99726e46883SJohn Baldwin 	for (p = sbni_headlist, p_prev = NULL; p; p_prev = p, p = p->link) {
99826e46883SJohn Baldwin 		if (rman_get_start(p->io_res) == rman_get_start(sc->io_res) + 4 ||
99926e46883SJohn Baldwin 		    rman_get_start(p->io_res) == rman_get_start(sc->io_res) - 4) {
100026e46883SJohn Baldwin 			p->slave_sc = sc;
100126e46883SJohn Baldwin 			if (p_prev)
100226e46883SJohn Baldwin 				p_prev->link = p->link;
100326e46883SJohn Baldwin 			else
100426e46883SJohn Baldwin 				sbni_headlist = p->link;
100526e46883SJohn Baldwin 			mtx_unlock(&headlist_lock);
100626e46883SJohn Baldwin 			return p;
100726e46883SJohn Baldwin 		}
100826e46883SJohn Baldwin 	}
100926e46883SJohn Baldwin 	mtx_unlock(&headlist_lock);
101026e46883SJohn Baldwin 
101126e46883SJohn Baldwin 	return (NULL);
101226e46883SJohn Baldwin }
101326e46883SJohn Baldwin 
101426e46883SJohn Baldwin #endif	/* SBNI_DUAL_COMPOUND */
101526e46883SJohn Baldwin 
101626e46883SJohn Baldwin /* Receive level auto-selection */
101726e46883SJohn Baldwin 
101826e46883SJohn Baldwin static void
change_level(struct sbni_softc * sc)101926e46883SJohn Baldwin change_level(struct sbni_softc *sc)
102026e46883SJohn Baldwin {
102126e46883SJohn Baldwin 	if (sc->delta_rxl == 0)		/* do not auto-negotiate RxL */
102226e46883SJohn Baldwin 		return;
102326e46883SJohn Baldwin 
102426e46883SJohn Baldwin 	if (sc->cur_rxl_index == 0)
102526e46883SJohn Baldwin 		sc->delta_rxl = 1;
102626e46883SJohn Baldwin 	else if (sc->cur_rxl_index == 15)
102726e46883SJohn Baldwin 		sc->delta_rxl = -1;
102826e46883SJohn Baldwin 	else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
102926e46883SJohn Baldwin 		sc->delta_rxl = -sc->delta_rxl;
103026e46883SJohn Baldwin 
103126e46883SJohn Baldwin 	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
103226e46883SJohn Baldwin 	sbni_inb(sc, CSR0);	/* it needed for PCI cards */
103326e46883SJohn Baldwin 	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
103426e46883SJohn Baldwin 
103526e46883SJohn Baldwin 	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
103626e46883SJohn Baldwin 	sc->cur_rxl_rcvd  = 0;
103726e46883SJohn Baldwin }
103826e46883SJohn Baldwin 
103926e46883SJohn Baldwin static void
timeout_change_level(struct sbni_softc * sc)104026e46883SJohn Baldwin timeout_change_level(struct sbni_softc *sc)
104126e46883SJohn Baldwin {
104226e46883SJohn Baldwin 	sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
104326e46883SJohn Baldwin 	if (++sc->timeout_rxl >= 4)
104426e46883SJohn Baldwin 		sc->timeout_rxl = 0;
104526e46883SJohn Baldwin 
104626e46883SJohn Baldwin 	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
104726e46883SJohn Baldwin 	sbni_inb(sc, CSR0);
104826e46883SJohn Baldwin 	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
104926e46883SJohn Baldwin 
105026e46883SJohn Baldwin 	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
105126e46883SJohn Baldwin 	sc->cur_rxl_rcvd  = 0;
105226e46883SJohn Baldwin }
105326e46883SJohn Baldwin 
105426e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
105526e46883SJohn Baldwin 
105626e46883SJohn Baldwin /*
105726e46883SJohn Baldwin  * Process an ioctl request. This code needs some work - it looks
105826e46883SJohn Baldwin  *	pretty ugly.
105926e46883SJohn Baldwin  */
106026e46883SJohn Baldwin 
106126e46883SJohn Baldwin static int
sbni_ioctl(if_t ifp,u_long command,caddr_t data)10623fba06adSJustin Hibbits sbni_ioctl(if_t ifp, u_long command, caddr_t data)
106326e46883SJohn Baldwin {
106426e46883SJohn Baldwin 	struct sbni_softc *sc;
106526e46883SJohn Baldwin 	struct ifreq *ifr;
106626e46883SJohn Baldwin 	struct thread *td;
106726e46883SJohn Baldwin 	struct sbni_in_stats *in_stats;
106826e46883SJohn Baldwin 	struct sbni_flags flags;
106926e46883SJohn Baldwin 	int error;
107026e46883SJohn Baldwin 
10713fba06adSJustin Hibbits 	sc = if_getsoftc(ifp);
107226e46883SJohn Baldwin 	ifr = (struct ifreq *)data;
107326e46883SJohn Baldwin 	td = curthread;
107426e46883SJohn Baldwin 	error = 0;
107526e46883SJohn Baldwin 
107626e46883SJohn Baldwin 	switch (command) {
107726e46883SJohn Baldwin 	case SIOCSIFFLAGS:
107826e46883SJohn Baldwin 		/*
107926e46883SJohn Baldwin 		 * If the interface is marked up and stopped, then start it.
108026e46883SJohn Baldwin 		 * If it is marked down and running, then stop it.
108126e46883SJohn Baldwin 		 */
108226e46883SJohn Baldwin 		SBNI_LOCK(sc);
10833fba06adSJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
10843fba06adSJustin Hibbits 			if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
108526e46883SJohn Baldwin 				sbni_init_locked(sc);
108626e46883SJohn Baldwin 		} else {
10873fba06adSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
108826e46883SJohn Baldwin 				sbni_stop(sc);
108926e46883SJohn Baldwin 			}
109026e46883SJohn Baldwin 		}
109126e46883SJohn Baldwin 		SBNI_UNLOCK(sc);
109226e46883SJohn Baldwin 		break;
109326e46883SJohn Baldwin 
109426e46883SJohn Baldwin 	case SIOCADDMULTI:
109526e46883SJohn Baldwin 	case SIOCDELMULTI:
109626e46883SJohn Baldwin 		/*
109726e46883SJohn Baldwin 		 * Multicast list has changed; set the hardware filter
109826e46883SJohn Baldwin 		 * accordingly.
109926e46883SJohn Baldwin 		 */
110026e46883SJohn Baldwin 		error = 0;
110126e46883SJohn Baldwin 		/* if (ifr == NULL)
110226e46883SJohn Baldwin 			error = EAFNOSUPPORT; */
110326e46883SJohn Baldwin 		break;
110426e46883SJohn Baldwin 
110526e46883SJohn Baldwin 		/*
110626e46883SJohn Baldwin 		 * SBNI specific ioctl
110726e46883SJohn Baldwin 		 */
110826e46883SJohn Baldwin 	case SIOCGHWFLAGS:	/* get flags */
110926e46883SJohn Baldwin 		SBNI_LOCK(sc);
11103fba06adSJustin Hibbits 		bcopy((caddr_t)if_getlladdr(sc->ifp)+3, (caddr_t) &flags, 3);
111126e46883SJohn Baldwin 		flags.rxl = sc->cur_rxl_index;
111226e46883SJohn Baldwin 		flags.rate = sc->csr1.rate;
111326e46883SJohn Baldwin 		flags.fixed_rxl = (sc->delta_rxl == 0);
111426e46883SJohn Baldwin 		flags.fixed_rate = 1;
111526e46883SJohn Baldwin 		SBNI_UNLOCK(sc);
11162f471660SBrooks Davis 		bcopy(&flags, &ifr->ifr_ifru, sizeof(flags));
111726e46883SJohn Baldwin 		break;
111826e46883SJohn Baldwin 
111926e46883SJohn Baldwin 	case SIOCGINSTATS:
112026e46883SJohn Baldwin 		in_stats = malloc(sizeof(struct sbni_in_stats), M_DEVBUF,
112126e46883SJohn Baldwin 		    M_WAITOK);
112226e46883SJohn Baldwin 		SBNI_LOCK(sc);
112326e46883SJohn Baldwin 		bcopy(&sc->in_stats, in_stats, sizeof(struct sbni_in_stats));
112426e46883SJohn Baldwin 		SBNI_UNLOCK(sc);
11258f4dfca1SBrooks Davis 		error = copyout(in_stats, ifr_data_get_ptr(ifr),
112626e46883SJohn Baldwin 		    sizeof(struct sbni_in_stats));
112726e46883SJohn Baldwin 		free(in_stats, M_DEVBUF);
112826e46883SJohn Baldwin 		break;
112926e46883SJohn Baldwin 
113026e46883SJohn Baldwin 	case SIOCSHWFLAGS:	/* set flags */
113126e46883SJohn Baldwin 		/* root only */
113226e46883SJohn Baldwin 		error = priv_check(td, PRIV_DRIVER);
113326e46883SJohn Baldwin 		if (error)
113426e46883SJohn Baldwin 			break;
11352f471660SBrooks Davis 		bcopy(&ifr->ifr_ifru, &flags, sizeof(flags));
113626e46883SJohn Baldwin 		SBNI_LOCK(sc);
113726e46883SJohn Baldwin 		if (flags.fixed_rxl) {
113826e46883SJohn Baldwin 			sc->delta_rxl = 0;
113926e46883SJohn Baldwin 			sc->cur_rxl_index = flags.rxl;
114026e46883SJohn Baldwin 		} else {
114126e46883SJohn Baldwin 			sc->delta_rxl = DEF_RXL_DELTA;
114226e46883SJohn Baldwin 			sc->cur_rxl_index = DEF_RXL;
114326e46883SJohn Baldwin 		}
114426e46883SJohn Baldwin 		sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
114526e46883SJohn Baldwin 		sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
114626e46883SJohn Baldwin 		if (flags.mac_addr)
114726e46883SJohn Baldwin 			bcopy((caddr_t) &flags,
11483fba06adSJustin Hibbits 			      (caddr_t) if_getlladdr(sc->ifp)+3, 3);
114926e46883SJohn Baldwin 
115026e46883SJohn Baldwin 		/* Don't be afraid... */
115126e46883SJohn Baldwin 		sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
115226e46883SJohn Baldwin 		SBNI_UNLOCK(sc);
115326e46883SJohn Baldwin 		break;
115426e46883SJohn Baldwin 
115526e46883SJohn Baldwin 	case SIOCRINSTATS:
115626e46883SJohn Baldwin 		SBNI_LOCK(sc);
115726e46883SJohn Baldwin 		if (!(error = priv_check(td, PRIV_DRIVER)))	/* root only */
115826e46883SJohn Baldwin 			bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
115926e46883SJohn Baldwin 		SBNI_UNLOCK(sc);
116026e46883SJohn Baldwin 		break;
116126e46883SJohn Baldwin 
116226e46883SJohn Baldwin 	default:
116326e46883SJohn Baldwin 		error = ether_ioctl(ifp, command, data);
116426e46883SJohn Baldwin 		break;
116526e46883SJohn Baldwin 	}
116626e46883SJohn Baldwin 
116726e46883SJohn Baldwin 	return (error);
116826e46883SJohn Baldwin }
116926e46883SJohn Baldwin 
117026e46883SJohn Baldwin /* -------------------------------------------------------------------------- */
117126e46883SJohn Baldwin 
117226e46883SJohn Baldwin static u_int32_t
calc_crc32(u_int32_t crc,caddr_t p,u_int len)117326e46883SJohn Baldwin calc_crc32(u_int32_t crc, caddr_t p, u_int len)
117426e46883SJohn Baldwin {
117526e46883SJohn Baldwin 	while (len--)
117626e46883SJohn Baldwin 		crc = CRC32(*p++, crc);
117726e46883SJohn Baldwin 
117826e46883SJohn Baldwin 	return (crc);
117926e46883SJohn Baldwin }
118026e46883SJohn Baldwin 
118126e46883SJohn Baldwin static u_int32_t crc32tab[] __aligned(8) = {
118226e46883SJohn Baldwin 	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
118326e46883SJohn Baldwin 	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
118426e46883SJohn Baldwin 	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
118526e46883SJohn Baldwin 	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
118626e46883SJohn Baldwin 	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
118726e46883SJohn Baldwin 	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
118826e46883SJohn Baldwin 	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
118926e46883SJohn Baldwin 	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
119026e46883SJohn Baldwin 	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
119126e46883SJohn Baldwin 	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
119226e46883SJohn Baldwin 	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
119326e46883SJohn Baldwin 	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
119426e46883SJohn Baldwin 	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
119526e46883SJohn Baldwin 	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
119626e46883SJohn Baldwin 	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
119726e46883SJohn Baldwin 	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
119826e46883SJohn Baldwin 	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
119926e46883SJohn Baldwin 	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
120026e46883SJohn Baldwin 	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
120126e46883SJohn Baldwin 	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
120226e46883SJohn Baldwin 	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
120326e46883SJohn Baldwin 	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
120426e46883SJohn Baldwin 	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
120526e46883SJohn Baldwin 	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
120626e46883SJohn Baldwin 	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
120726e46883SJohn Baldwin 	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
120826e46883SJohn Baldwin 	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
120926e46883SJohn Baldwin 	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
121026e46883SJohn Baldwin 	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
121126e46883SJohn Baldwin 	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
121226e46883SJohn Baldwin 	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
121326e46883SJohn Baldwin 	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
121426e46883SJohn Baldwin 	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
121526e46883SJohn Baldwin 	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
121626e46883SJohn Baldwin 	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
121726e46883SJohn Baldwin 	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
121826e46883SJohn Baldwin 	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
121926e46883SJohn Baldwin 	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
122026e46883SJohn Baldwin 	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
122126e46883SJohn Baldwin 	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
122226e46883SJohn Baldwin 	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
122326e46883SJohn Baldwin 	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
122426e46883SJohn Baldwin 	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
122526e46883SJohn Baldwin 	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
122626e46883SJohn Baldwin 	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
122726e46883SJohn Baldwin 	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
122826e46883SJohn Baldwin 	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
122926e46883SJohn Baldwin 	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
123026e46883SJohn Baldwin 	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
123126e46883SJohn Baldwin 	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
123226e46883SJohn Baldwin 	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
123326e46883SJohn Baldwin 	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
123426e46883SJohn Baldwin 	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
123526e46883SJohn Baldwin 	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
123626e46883SJohn Baldwin 	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
123726e46883SJohn Baldwin 	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
123826e46883SJohn Baldwin 	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
123926e46883SJohn Baldwin 	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
124026e46883SJohn Baldwin 	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
124126e46883SJohn Baldwin 	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
124226e46883SJohn Baldwin 	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
124326e46883SJohn Baldwin 	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
124426e46883SJohn Baldwin 	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
124526e46883SJohn Baldwin 	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
124626e46883SJohn Baldwin };
1247