if_sbni.c revision 147256
1262395Sbapt/*-
2262395Sbapt * Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
3262395Sbapt * Author: Denis I.Timofeev <timofeev@granch.ru>
4262395Sbapt *
5262395Sbapt * Redistributon and use in source and binary forms, with or without
6262395Sbapt * modification, are permitted provided that the following conditions
7262395Sbapt * are met:
8262395Sbapt * 1. Redistributions of source code must retain the above copyright
9262395Sbapt *    notice unmodified, this list of conditions, and the following
10262395Sbapt *    disclaimer.
11262395Sbapt * 2. Redistributions in binary form must reproduce the above copyright
12262395Sbapt *    notice, this list of conditions and the following disclaimer in the
13262395Sbapt *    documentation and/or other materials provided with the distribution.
14262395Sbapt *
15262395Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16262395Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17262395Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18262395Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19262395Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20262395Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21262395Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22262395Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23262395Sbapt * LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
24262395Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25262395Sbapt * SUCH DAMAGE.
26262395Sbapt *
27262395Sbapt */
28262395Sbapt
29262395Sbapt#include <sys/cdefs.h>
30262395Sbapt__FBSDID("$FreeBSD: head/sys/dev/sbni/if_sbni.c 147256 2005-06-10 16:49:24Z brooks $");
31262395Sbapt
32262395Sbapt/*
33262395Sbapt * Device driver for Granch SBNI12 leased line adapters
34262395Sbapt *
35262395Sbapt * Revision 2.0.0  1997/08/06
36262395Sbapt * Initial revision by Alexey Zverev
37262395Sbapt *
38262395Sbapt * Revision 2.0.1 1997/08/11
39262395Sbapt * Additional internal statistics support (tx statistics)
40262395Sbapt *
41262395Sbapt * Revision 2.0.2 1997/11/05
42262395Sbapt * if_bpf bug has been fixed
43262395Sbapt *
44262395Sbapt * Revision 2.0.3 1998/12/20
45262395Sbapt * Memory leakage has been eliminated in
46262395Sbapt * the sbni_st and sbni_timeout routines.
47262395Sbapt *
48262395Sbapt * Revision 3.0 2000/08/10 by Yaroslav Polyakov
49262395Sbapt * Support for PCI cards. 4.1 modification.
50262395Sbapt *
51262395Sbapt * Revision 3.1 2000/09/12
52262395Sbapt * Removed extra #defines around bpf functions
53262395Sbapt *
54262395Sbapt * Revision 4.0 2000/11/23 by Denis Timofeev
55262395Sbapt * Completely redesigned the buffer management
56262395Sbapt *
57262395Sbapt * Revision 4.1 2001/01/21
58262395Sbapt * Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
59262395Sbapt *
60262395Sbapt * Written with reference to NE2000 driver developed by David Greenman.
61262395Sbapt */
62262395Sbapt
63262395Sbapt
64262395Sbapt#include <sys/param.h>
65262395Sbapt#include <sys/systm.h>
66262395Sbapt#include <sys/socket.h>
67262395Sbapt#include <sys/sockio.h>
68262395Sbapt#include <sys/mbuf.h>
69262395Sbapt#include <sys/kernel.h>
70262395Sbapt#include <sys/proc.h>
71262395Sbapt#include <sys/callout.h>
72262395Sbapt#include <sys/syslog.h>
73262395Sbapt#include <sys/random.h>
74262395Sbapt
75262395Sbapt#include <machine/bus.h>
76262395Sbapt#include <sys/rman.h>
77262395Sbapt#include <machine/resource.h>
78262395Sbapt
79262395Sbapt#include <net/if.h>
80262395Sbapt#include <net/ethernet.h>
81262395Sbapt#include <net/if_arp.h>
82262395Sbapt#include <net/bpf.h>
83262395Sbapt#include <net/if_types.h>
84262395Sbapt
85262395Sbapt#include <dev/sbni/if_sbnireg.h>
86262395Sbapt#include <dev/sbni/if_sbnivar.h>
87262395Sbapt
88262395Sbapt#define ASM_CRC 1
89262395Sbapt
90262395Sbaptstatic void	sbni_init(void *);
91262395Sbaptstatic void	sbni_start(struct ifnet *);
92262395Sbaptstatic int	sbni_ioctl(struct ifnet *, u_long, caddr_t);
93262395Sbaptstatic void	sbni_watchdog(struct ifnet *);
94262395Sbaptstatic void	sbni_stop(struct sbni_softc *);
95262395Sbaptstatic void	handle_channel(struct sbni_softc *);
96262395Sbapt
97262395Sbaptstatic void	card_start(struct sbni_softc *);
98262395Sbaptstatic int	recv_frame(struct sbni_softc *);
99262395Sbaptstatic void	send_frame(struct sbni_softc *);
100262395Sbaptstatic int	upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
101262395Sbaptstatic int	skip_tail(struct sbni_softc *, u_int, u_int32_t);
102262395Sbaptstatic void	interpret_ack(struct sbni_softc *, u_int);
103262395Sbaptstatic void	download_data(struct sbni_softc *, u_int32_t *);
104262395Sbaptstatic void	prepare_to_send(struct sbni_softc *);
105262395Sbaptstatic void	drop_xmit_queue(struct sbni_softc *);
106262395Sbaptstatic int	get_rx_buf(struct sbni_softc *);
107262395Sbaptstatic void	indicate_pkt(struct sbni_softc *);
108262395Sbaptstatic void	change_level(struct sbni_softc *);
109262395Sbaptstatic int	check_fhdr(struct sbni_softc *, u_int *, u_int *,
110262395Sbapt			   u_int *, u_int *, u_int32_t *);
111262395Sbaptstatic int	append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
112262395Sbaptstatic void	timeout_change_level(struct sbni_softc *);
113262395Sbaptstatic void	send_frame_header(struct sbni_softc *, u_int32_t *);
114262395Sbaptstatic void	set_initial_values(struct sbni_softc *, struct sbni_flags);
115262395Sbapt
116262395Sbaptstatic u_int32_t	calc_crc32(u_int32_t, caddr_t, u_int);
117262395Sbaptstatic timeout_t	sbni_timeout;
118262395Sbapt
119262395Sbaptstatic __inline u_char	sbni_inb(struct sbni_softc *, enum sbni_reg);
120262395Sbaptstatic __inline void	sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
121262395Sbaptstatic __inline void	sbni_insb(struct sbni_softc *, u_char *, u_int);
122262395Sbaptstatic __inline void	sbni_outsb(struct sbni_softc *, u_char *, u_int);
123262395Sbapt
124262395Sbaptstatic u_int32_t crc32tab[];
125262395Sbapt
126262395Sbapt#ifdef SBNI_DUAL_COMPOUND
127262395Sbaptstruct sbni_softc *sbni_headlist;
128262395Sbapt#endif
129262395Sbapt
130262395Sbaptu_int32_t next_sbni_unit;
131262395Sbapt
132262395Sbapt/* -------------------------------------------------------------------------- */
133262395Sbapt
134262395Sbaptstatic __inline u_char
135262395Sbaptsbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
136262395Sbapt{
137262395Sbapt	return bus_space_read_1(
138262395Sbapt	    rman_get_bustag(sc->io_res),
139262395Sbapt	    rman_get_bushandle(sc->io_res),
140262395Sbapt	    sc->io_off + reg);
141262395Sbapt}
142262395Sbapt
143262395Sbaptstatic __inline void
144262395Sbaptsbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
145262395Sbapt{
146262395Sbapt	bus_space_write_1(
147262395Sbapt	    rman_get_bustag(sc->io_res),
148262395Sbapt	    rman_get_bushandle(sc->io_res),
149262395Sbapt	    sc->io_off + reg, value);
150262395Sbapt}
151262395Sbapt
152262395Sbaptstatic __inline void
153262395Sbaptsbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
154262395Sbapt{
155262395Sbapt	bus_space_read_multi_1(
156262395Sbapt	    rman_get_bustag(sc->io_res),
157262395Sbapt	    rman_get_bushandle(sc->io_res),
158262395Sbapt	    sc->io_off + DAT, to, len);
159262395Sbapt}
160262395Sbapt
161262395Sbaptstatic __inline void
162262395Sbaptsbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
163262395Sbapt{
164262395Sbapt	bus_space_write_multi_1(
165262395Sbapt	    rman_get_bustag(sc->io_res),
166262395Sbapt	    rman_get_bushandle(sc->io_res),
167262395Sbapt	    sc->io_off + DAT, from, len);
168262395Sbapt}
169262395Sbapt
170262395Sbapt
171262395Sbapt/*
172262395Sbapt	Valid combinations in CSR0 (for probing):
173262395Sbapt
174262395Sbapt	VALID_DECODER	0000,0011,1011,1010
175262395Sbapt
176262395Sbapt				    	; 0   ; -
177262395Sbapt				TR_REQ	; 1   ; +
178262395Sbapt			TR_RDY	    	; 2   ; -
179262395Sbapt			TR_RDY	TR_REQ	; 3   ; +
180262395Sbapt		BU_EMP		    	; 4   ; +
181262395Sbapt		BU_EMP	     	TR_REQ	; 5   ; +
182262395Sbapt		BU_EMP	TR_RDY	    	; 6   ; -
183262395Sbapt		BU_EMP	TR_RDY	TR_REQ	; 7   ; +
184262395Sbapt	RC_RDY 		     		; 8   ; +
185262395Sbapt	RC_RDY			TR_REQ	; 9   ; +
186262395Sbapt	RC_RDY		TR_RDY		; 10  ; -
187262395Sbapt	RC_RDY		TR_RDY	TR_REQ	; 11  ; -
188262395Sbapt	RC_RDY	BU_EMP			; 12  ; -
189262395Sbapt	RC_RDY	BU_EMP		TR_REQ	; 13  ; -
190262395Sbapt	RC_RDY	BU_EMP	TR_RDY		; 14  ; -
191262395Sbapt	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; -
192262395Sbapt*/
193262395Sbapt
194262395Sbapt#define VALID_DECODER	(2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
195262395Sbapt
196262395Sbapt
197262395Sbaptint
198262395Sbaptsbni_probe(struct sbni_softc *sc)
199262395Sbapt{
200262395Sbapt	u_char csr0;
201262395Sbapt
202262395Sbapt	csr0 = sbni_inb(sc, CSR0);
203262395Sbapt	if (csr0 != 0xff && csr0 != 0x00) {
204262395Sbapt		csr0 &= ~EN_INT;
205262395Sbapt		if (csr0 & BU_EMP)
206262395Sbapt			csr0 |= EN_INT;
207262395Sbapt
208262395Sbapt		if (VALID_DECODER & (1 << (csr0 >> 4)))
209262395Sbapt			return (0);
210262395Sbapt	}
211262395Sbapt
212262395Sbapt	return (ENXIO);
213262395Sbapt}
214262395Sbapt
215262395Sbapt
216262395Sbapt/*
217262395Sbapt * Install interface into kernel networking data structures
218262395Sbapt */
219262395Sbaptvoid
220262395Sbaptsbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
221262395Sbapt{
222262395Sbapt	struct ifnet *ifp;
223262395Sbapt	u_char csr0;
224262395Sbapt
225262395Sbapt	ifp = sc->ifp = if_alloc(IFT_ETHER);
226262395Sbapt	if (ifp == NULL)
227262395Sbapt		panic("sbni%d: can not if_alloc()", unit);
228262395Sbapt	sbni_outb(sc, CSR0, 0);
229262395Sbapt	set_initial_values(sc, flags);
230262395Sbapt
231262395Sbapt	callout_handle_init(&sc->wch);
232262395Sbapt	/* Initialize ifnet structure */
233262395Sbapt	ifp->if_softc	= sc;
234262395Sbapt	if_initname(ifp, "sbni", unit);
235262395Sbapt	ifp->if_init	= sbni_init;
236262395Sbapt	ifp->if_start	= sbni_start;
237262395Sbapt	ifp->if_ioctl	= sbni_ioctl;
238262395Sbapt	ifp->if_watchdog	= sbni_watchdog;
239266636Sbapt	ifp->if_snd.ifq_maxlen	= IFQ_MAXLEN;
240266636Sbapt
241266636Sbapt	/* report real baud rate */
242262395Sbapt	csr0 = sbni_inb(sc, CSR0);
243262395Sbapt	ifp->if_baudrate =
244262395Sbapt		(csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
245262395Sbapt
246262395Sbapt	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
247262395Sbapt	    IFF_NEEDSGIANT;
248262395Sbapt	ether_ifattach(ifp, sc->enaddr);
249262395Sbapt	/* device attach does transition from UNCONFIGURED to IDLE state */
250262395Sbapt
251262395Sbapt	if_printf(ifp, "speed %ld, rxl ", ifp->if_baudrate);
252262395Sbapt	if (sc->delta_rxl)
253262395Sbapt		printf("auto\n");
254262395Sbapt	else
255262395Sbapt		printf("%d (fixed)\n", sc->cur_rxl_index);
256262395Sbapt}
257262395Sbapt
258262395Sbapt/* -------------------------------------------------------------------------- */
259262395Sbapt
260262395Sbaptstatic void
261262395Sbaptsbni_init(void *xsc)
262262395Sbapt{
263262395Sbapt	struct sbni_softc *sc;
264266636Sbapt	struct ifnet *ifp;
265266636Sbapt	int  s;
266266636Sbapt
267266636Sbapt	sc = (struct sbni_softc *)xsc;
268266636Sbapt	ifp = sc->ifp;
269266636Sbapt
270266636Sbapt	/*
271266636Sbapt	 * kludge to avoid multiple initialization when more than once
272266636Sbapt	 * protocols configured
273266636Sbapt	 */
274266636Sbapt	if (ifp->if_flags & IFF_RUNNING)
275266636Sbapt		return;
276266636Sbapt
277262395Sbapt	s = splimp();
278262395Sbapt	ifp->if_timer = 0;
279262395Sbapt	card_start(sc);
280262395Sbapt	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
281262395Sbapt
282262395Sbapt	ifp->if_flags |= IFF_RUNNING;
283262395Sbapt	ifp->if_flags &= ~IFF_OACTIVE;
284262395Sbapt
285262395Sbapt	/* attempt to start output */
286262395Sbapt	sbni_start(ifp);
287262395Sbapt	splx(s);
288262395Sbapt}
289262395Sbapt
290266636Sbapt
291266636Sbaptstatic void
292262395Sbaptsbni_start(struct ifnet *ifp)
293262395Sbapt{
294262395Sbapt	struct sbni_softc *sc = ifp->if_softc;
295262395Sbapt	if (sc->tx_frameno == 0)
296262395Sbapt		prepare_to_send(sc);
297262395Sbapt}
298262395Sbapt
299262395Sbapt
300262395Sbaptstatic void
301262395Sbaptsbni_stop(struct sbni_softc *sc)
302266636Sbapt{
303266636Sbapt	sbni_outb(sc, CSR0, 0);
304262395Sbapt	drop_xmit_queue(sc);
305262395Sbapt
306262395Sbapt	if (sc->rx_buf_p) {
307262395Sbapt		m_freem(sc->rx_buf_p);
308262395Sbapt		sc->rx_buf_p = NULL;
309262395Sbapt	}
310262395Sbapt
311262395Sbapt	untimeout(sbni_timeout, sc, sc->wch);
312262395Sbapt	sc->wch.callout = NULL;
313262395Sbapt}
314262395Sbapt
315262395Sbapt/* -------------------------------------------------------------------------- */
316262395Sbapt
317262395Sbapt/* interrupt handler */
318262395Sbapt
319262395Sbapt/*
320262395Sbapt * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
321262395Sbapt * be looked as two independent single-channel devices. Every channel seems
322262395Sbapt * as Ethernet interface but interrupt handler must be common. Really, first
323262395Sbapt * channel ("master") driver only registers the handler. In it's struct softc
324262395Sbapt * it has got pointer to "slave" channel's struct softc and handles that's
325262395Sbapt * interrupts too.
326262395Sbapt *	softc of successfully attached ISA SBNI boards is linked to list.
327262395Sbapt * While next board driver is initialized, it scans this list. If one
328262395Sbapt * has found softc with same irq and ioaddr different by 4 then it assumes
329262395Sbapt * this board to be "master".
330262395Sbapt */
331262395Sbapt
332262395Sbaptvoid
333262395Sbaptsbni_intr(void *arg)
334262395Sbapt{
335262395Sbapt	struct sbni_softc *sc;
336262395Sbapt	int repeat;
337262395Sbapt
338262395Sbapt	sc = (struct sbni_softc *)arg;
339262395Sbapt
340262395Sbapt	do {
341262395Sbapt		repeat = 0;
342262395Sbapt		if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
343262395Sbapt			handle_channel(sc);
344262395Sbapt			repeat = 1;
345262395Sbapt		}
346262395Sbapt		if (sc->slave_sc && 	/* second channel present */
347262395Sbapt		    (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY))) {
348262395Sbapt			handle_channel(sc->slave_sc);
349266636Sbapt			repeat = 1;
350262395Sbapt		}
351262395Sbapt	} while (repeat);
352266636Sbapt}
353266636Sbapt
354262395Sbapt
355266636Sbaptstatic void
356262395Sbapthandle_channel(struct sbni_softc *sc)
357262395Sbapt{
358262395Sbapt	int req_ans;
359262395Sbapt	u_char csr0;
360262395Sbapt
361262395Sbapt	sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
362262395Sbapt
363262395Sbapt	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
364262395Sbapt	for (;;) {
365262395Sbapt		csr0 = sbni_inb(sc, CSR0);
366262395Sbapt		if ((csr0 & (RC_RDY | TR_RDY)) == 0)
367266636Sbapt			break;
368262395Sbapt
369262395Sbapt		req_ans = !(sc->state & FL_PREV_OK);
370262395Sbapt
371262395Sbapt		if (csr0 & RC_RDY)
372262395Sbapt			req_ans = recv_frame(sc);
373262395Sbapt
374262395Sbapt		/*
375262395Sbapt		 * TR_RDY always equals 1 here because we have owned the marker,
376262395Sbapt		 * and we set TR_REQ when disabled interrupts
377262395Sbapt		 */
378262395Sbapt		csr0 = sbni_inb(sc, CSR0);
379262395Sbapt		if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
380262395Sbapt			printf("sbni: internal error!\n");
381262395Sbapt
382262395Sbapt		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
383262395Sbapt		if (req_ans || sc->tx_frameno != 0)
384266636Sbapt			send_frame(sc);
385266636Sbapt		else {
386266636Sbapt			/* send the marker without any data */
387266636Sbapt			sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
388266636Sbapt		}
389266636Sbapt	}
390266636Sbapt
391266636Sbapt	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
392266636Sbapt}
393266636Sbapt
394266636Sbapt
395266636Sbapt/*
396266636Sbapt * Routine returns 1 if it need to acknoweledge received frame.
397266636Sbapt * Empty frame received without errors won't be acknoweledged.
398266636Sbapt */
399266636Sbapt
400262395Sbaptstatic int
401262395Sbaptrecv_frame(struct sbni_softc *sc)
402262395Sbapt{
403262395Sbapt	u_int32_t crc;
404262395Sbapt	u_int framelen, frameno, ack;
405262395Sbapt	u_int is_first, frame_ok;
406262395Sbapt
407262395Sbapt	crc = CRC32_INITIAL;
408262395Sbapt	if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
409262395Sbapt		frame_ok = framelen > 4 ?
410262395Sbapt		    upload_data(sc, framelen, frameno, is_first, crc) :
411262395Sbapt		    skip_tail(sc, framelen, crc);
412262395Sbapt		if (frame_ok)
413262395Sbapt			interpret_ack(sc, ack);
414262395Sbapt	} else
415262395Sbapt		frame_ok = 0;
416262395Sbapt
417262395Sbapt	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
418262395Sbapt	if (frame_ok) {
419262395Sbapt		sc->state |= FL_PREV_OK;
420262395Sbapt		if (framelen > 4)
421262395Sbapt			sc->in_stats.all_rx_number++;
422262395Sbapt	} else {
423262395Sbapt		sc->state &= ~FL_PREV_OK;
424262395Sbapt		change_level(sc);
425262395Sbapt		sc->in_stats.all_rx_number++;
426262395Sbapt		sc->in_stats.bad_rx_number++;
427262395Sbapt	}
428262395Sbapt
429262395Sbapt	return (!frame_ok || framelen > 4);
430262395Sbapt}
431262395Sbapt
432262395Sbapt
433262395Sbaptstatic void
434262395Sbaptsend_frame(struct sbni_softc *sc)
435262395Sbapt{
436262395Sbapt	u_int32_t crc;
437262395Sbapt	u_char csr0;
438262395Sbapt
439262395Sbapt	crc = CRC32_INITIAL;
440262395Sbapt	if (sc->state & FL_NEED_RESEND) {
441262395Sbapt
442262395Sbapt		/* if frame was sended but not ACK'ed - resend it */
443262395Sbapt		if (sc->trans_errors) {
444262395Sbapt			sc->trans_errors--;
445262395Sbapt			if (sc->framelen != 0)
446262395Sbapt				sc->in_stats.resend_tx_number++;
447262395Sbapt		} else {
448262395Sbapt			/* cannot xmit with many attempts */
449262395Sbapt			drop_xmit_queue(sc);
450262395Sbapt			goto do_send;
451262395Sbapt		}
452262395Sbapt	} else
453262395Sbapt		sc->trans_errors = TR_ERROR_COUNT;
454262395Sbapt
455262395Sbapt	send_frame_header(sc, &crc);
456262395Sbapt	sc->state |= FL_NEED_RESEND;
457262395Sbapt	/*
458262395Sbapt	 * FL_NEED_RESEND will be cleared after ACK, but if empty
459262395Sbapt	 * frame sended then in prepare_to_send next frame
460262395Sbapt	 */
461262395Sbapt
462262395Sbapt
463262395Sbapt	if (sc->framelen) {
464262395Sbapt		download_data(sc, &crc);
465262395Sbapt		sc->in_stats.all_tx_number++;
466262395Sbapt		sc->state |= FL_WAIT_ACK;
467262395Sbapt	}
468262395Sbapt
469262395Sbapt	sbni_outsb(sc, (u_char *)&crc, sizeof crc);
470262395Sbapt
471262395Sbaptdo_send:
472262395Sbapt	csr0 = sbni_inb(sc, CSR0);
473262395Sbapt	sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
474262395Sbapt
475262395Sbapt	if (sc->tx_frameno) {
476262395Sbapt		/* next frame exists - request to send */
477262395Sbapt		sbni_outb(sc, CSR0, csr0 | TR_REQ);
478262395Sbapt	}
479262395Sbapt}
480262395Sbapt
481262395Sbapt
482262395Sbaptstatic void
483262395Sbaptdownload_data(struct sbni_softc *sc, u_int32_t *crc_p)
484262395Sbapt{
485262395Sbapt	struct mbuf *m;
486262395Sbapt	caddr_t	data_p;
487262395Sbapt	u_int data_len, pos, slice;
488262395Sbapt
489262395Sbapt	data_p = NULL;		/* initialized to avoid warn */
490262395Sbapt	pos = 0;
491262395Sbapt
492262395Sbapt	for (m = sc->tx_buf_p;  m != NULL && pos < sc->pktlen;  m = m->m_next) {
493262395Sbapt		if (pos + m->m_len > sc->outpos) {
494262395Sbapt			data_len = m->m_len - (sc->outpos - pos);
495262395Sbapt			data_p = mtod(m, caddr_t) + (sc->outpos - pos);
496262395Sbapt
497262395Sbapt			goto do_copy;
498262395Sbapt		} else
499262395Sbapt			pos += m->m_len;
500262395Sbapt	}
501262395Sbapt
502262395Sbapt	data_len = 0;
503262395Sbapt
504262395Sbaptdo_copy:
505262395Sbapt	pos = 0;
506262395Sbapt	do {
507262395Sbapt		if (data_len) {
508262395Sbapt			slice = min(data_len, sc->framelen - pos);
509262395Sbapt			sbni_outsb(sc, data_p, slice);
510262395Sbapt			*crc_p = calc_crc32(*crc_p, data_p, slice);
511262395Sbapt
512262395Sbapt			pos += slice;
513262395Sbapt			if (data_len -= slice)
514262395Sbapt				data_p += slice;
515262395Sbapt			else {
516262395Sbapt				do {
517262395Sbapt					m = m->m_next;
518262395Sbapt				} while (m != NULL && m->m_len == 0);
519262395Sbapt
520262395Sbapt				if (m) {
521262395Sbapt					data_len = m->m_len;
522262395Sbapt					data_p = mtod(m, caddr_t);
523262395Sbapt				}
524262395Sbapt			}
525262395Sbapt		} else {
526262395Sbapt			/* frame too short - zero padding */
527262395Sbapt
528262395Sbapt			pos = sc->framelen - pos;
529262395Sbapt			while (pos--) {
530262395Sbapt				sbni_outb(sc, DAT, 0);
531262395Sbapt				*crc_p = CRC32(0, *crc_p);
532262395Sbapt			}
533262395Sbapt			return;
534262395Sbapt		}
535262395Sbapt	} while (pos < sc->framelen);
536262395Sbapt}
537262395Sbapt
538262395Sbapt
539262395Sbaptstatic int
540262395Sbaptupload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
541262395Sbapt	    u_int is_first, u_int32_t crc)
542262395Sbapt{
543262395Sbapt	int frame_ok;
544262395Sbapt
545262395Sbapt	if (is_first) {
546262395Sbapt		sc->wait_frameno = frameno;
547262395Sbapt		sc->inppos = 0;
548262395Sbapt	}
549262395Sbapt
550262395Sbapt	if (sc->wait_frameno == frameno) {
551262395Sbapt
552262395Sbapt		if (sc->inppos + framelen  <=  ETHER_MAX_LEN) {
553262395Sbapt			frame_ok = append_frame_to_pkt(sc, framelen, crc);
554263648Sbapt
555263648Sbapt		/*
556263648Sbapt		 * if CRC is right but framelen incorrect then transmitter
557263648Sbapt		 * error was occured... drop entire packet
558262395Sbapt		 */
559262395Sbapt		} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
560262395Sbapt			sc->wait_frameno = 0;
561262395Sbapt			sc->inppos = 0;
562262395Sbapt			sc->ifp->if_ierrors++;
563262395Sbapt			/* now skip all frames until is_first != 0 */
564262395Sbapt		}
565262395Sbapt	} else
566262395Sbapt		frame_ok = skip_tail(sc, framelen, crc);
567262395Sbapt
568263648Sbapt	if (is_first && !frame_ok) {
569263648Sbapt		/*
570262395Sbapt		 * Frame has been violated, but we have stored
571262395Sbapt		 * is_first already... Drop entire packet.
572262395Sbapt		 */
573262395Sbapt		sc->wait_frameno = 0;
574263648Sbapt		sc->ifp->if_ierrors++;
575262395Sbapt	}
576262395Sbapt
577262395Sbapt	return (frame_ok);
578262395Sbapt}
579262395Sbapt
580262395Sbapt
581262395Sbaptstatic __inline void	send_complete(struct sbni_softc *);
582262395Sbapt
583262395Sbaptstatic __inline void
584262395Sbaptsend_complete(struct sbni_softc *sc)
585262395Sbapt{
586262395Sbapt	m_freem(sc->tx_buf_p);
587262395Sbapt	sc->tx_buf_p = NULL;
588262395Sbapt	sc->ifp->if_opackets++;
589262395Sbapt}
590262395Sbapt
591262395Sbapt
592262395Sbaptstatic void
593262395Sbaptinterpret_ack(struct sbni_softc *sc, u_int ack)
594262395Sbapt{
595262395Sbapt	if (ack == FRAME_SENT_OK) {
596262395Sbapt		sc->state &= ~FL_NEED_RESEND;
597262395Sbapt
598262395Sbapt		if (sc->state & FL_WAIT_ACK) {
599262395Sbapt			sc->outpos += sc->framelen;
600262395Sbapt
601262395Sbapt			if (--sc->tx_frameno) {
602262395Sbapt				sc->framelen = min(
603262395Sbapt				    sc->maxframe, sc->pktlen - sc->outpos);
604262395Sbapt			} else {
605262395Sbapt				send_complete(sc);
606262395Sbapt				prepare_to_send(sc);
607262395Sbapt			}
608262395Sbapt		}
609262395Sbapt	}
610262395Sbapt
611262395Sbapt	sc->state &= ~FL_WAIT_ACK;
612262395Sbapt}
613262395Sbapt
614262395Sbapt
615262395Sbapt/*
616262395Sbapt * Glue received frame with previous fragments of packet.
617262395Sbapt * Indicate packet when last frame would be accepted.
618262395Sbapt */
619262395Sbapt
620262395Sbaptstatic int
621262395Sbaptappend_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
622262395Sbapt{
623262395Sbapt	caddr_t p;
624262395Sbapt
625262395Sbapt	if (sc->inppos + framelen > ETHER_MAX_LEN)
626262395Sbapt		return (0);
627262395Sbapt
628262395Sbapt	if (!sc->rx_buf_p && !get_rx_buf(sc))
629262395Sbapt		return (0);
630262395Sbapt
631262395Sbapt	p = sc->rx_buf_p->m_data + sc->inppos;
632262395Sbapt	sbni_insb(sc, p, framelen);
633262395Sbapt	if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
634262395Sbapt		return (0);
635262395Sbapt
636262395Sbapt	sc->inppos += framelen - 4;
637262395Sbapt	if (--sc->wait_frameno == 0) {		/* last frame received */
638262395Sbapt		indicate_pkt(sc);
639262395Sbapt		sc->ifp->if_ipackets++;
640262395Sbapt	}
641262395Sbapt
642262395Sbapt	return (1);
643262395Sbapt}
644262395Sbapt
645262395Sbapt
646262395Sbapt/*
647262395Sbapt * Prepare to start output on adapter. Current priority must be set to splimp
648262395Sbapt * before this routine is called.
649262395Sbapt * Transmitter will be actually activated when marker has been accepted.
650262395Sbapt */
651262395Sbapt
652262395Sbaptstatic void
653262395Sbaptprepare_to_send(struct sbni_softc *sc)
654262395Sbapt{
655262395Sbapt	struct mbuf *m;
656262395Sbapt	u_int len;
657262395Sbapt
658262395Sbapt	/* sc->tx_buf_p == NULL here! */
659262395Sbapt	if (sc->tx_buf_p)
660262395Sbapt		printf("sbni: memory leak!\n");
661262395Sbapt
662262395Sbapt	sc->outpos = 0;
663262395Sbapt	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
664262395Sbapt
665262395Sbapt	for (;;) {
666262395Sbapt		IF_DEQUEUE(&sc->ifp->if_snd, sc->tx_buf_p);
667262395Sbapt		if (!sc->tx_buf_p) {
668262395Sbapt			/* nothing to transmit... */
669262395Sbapt			sc->pktlen     = 0;
670262395Sbapt			sc->tx_frameno = 0;
671262395Sbapt			sc->framelen   = 0;
672263648Sbapt			sc->ifp->if_flags &= ~IFF_OACTIVE;
673263648Sbapt			return;
674262395Sbapt		}
675262395Sbapt
676262395Sbapt		for (len = 0, m = sc->tx_buf_p;  m;  m = m->m_next)
677262395Sbapt			len += m->m_len;
678262395Sbapt
679262395Sbapt		if (len != 0)
680262395Sbapt			break;
681262395Sbapt		m_freem(sc->tx_buf_p);
682262395Sbapt	}
683262395Sbapt
684262395Sbapt	if (len < SBNI_MIN_LEN)
685262395Sbapt		len = SBNI_MIN_LEN;
686262395Sbapt
687262395Sbapt	sc->pktlen	= len;
688262395Sbapt	sc->tx_frameno	= (len + sc->maxframe - 1) / sc->maxframe;
689262395Sbapt	sc->framelen	= min(len, sc->maxframe);
690262395Sbapt
691262395Sbapt	sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
692262395Sbapt	sc->ifp->if_flags |= IFF_OACTIVE;
693262395Sbapt	BPF_MTAP(sc->ifp, sc->tx_buf_p);
694263648Sbapt}
695262395Sbapt
696262395Sbapt
697262395Sbaptstatic void
698262395Sbaptdrop_xmit_queue(struct sbni_softc *sc)
699262395Sbapt{
700262395Sbapt	struct mbuf *m;
701262395Sbapt
702262395Sbapt	if (sc->tx_buf_p) {
703262395Sbapt		m_freem(sc->tx_buf_p);
704262395Sbapt		sc->tx_buf_p = NULL;
705262395Sbapt		sc->ifp->if_oerrors++;
706262395Sbapt	}
707262395Sbapt
708262395Sbapt	for (;;) {
709262395Sbapt		IF_DEQUEUE(&sc->ifp->if_snd, m);
710262395Sbapt		if (m == NULL)
711262395Sbapt			break;
712262395Sbapt		m_freem(m);
713262395Sbapt		sc->ifp->if_oerrors++;
714262395Sbapt	}
715262395Sbapt
716262395Sbapt	sc->tx_frameno	= 0;
717262395Sbapt	sc->framelen	= 0;
718262395Sbapt	sc->outpos	= 0;
719262395Sbapt	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
720262395Sbapt	sc->ifp->if_flags &= ~IFF_OACTIVE;
721262395Sbapt}
722262395Sbapt
723262395Sbapt
724263648Sbaptstatic void
725262395Sbaptsend_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
726262395Sbapt{
727262395Sbapt	u_int32_t crc;
728262395Sbapt	u_int len_field;
729262395Sbapt	u_char value;
730262395Sbapt
731262395Sbapt	crc = *crc_p;
732262395Sbapt	len_field = sc->framelen + 6;	/* CRC + frameno + reserved */
733263648Sbapt
734262395Sbapt	if (sc->state & FL_NEED_RESEND)
735262395Sbapt		len_field |= FRAME_RETRY;	/* non-first attempt... */
736262395Sbapt
737262395Sbapt	if (sc->outpos == 0)
738262395Sbapt		len_field |= FRAME_FIRST;
739262395Sbapt
740262395Sbapt	len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
741262395Sbapt	sbni_outb(sc, DAT, SBNI_SIG);
742262395Sbapt
743262395Sbapt	value = (u_char)len_field;
744262395Sbapt	sbni_outb(sc, DAT, value);
745262395Sbapt	crc = CRC32(value, crc);
746262395Sbapt	value = (u_char)(len_field >> 8);
747262395Sbapt	sbni_outb(sc, DAT, value);
748262395Sbapt	crc = CRC32(value, crc);
749262395Sbapt
750262395Sbapt	sbni_outb(sc, DAT, sc->tx_frameno);
751262395Sbapt	crc = CRC32(sc->tx_frameno, crc);
752262395Sbapt	sbni_outb(sc, DAT, 0);
753263648Sbapt	crc = CRC32(0, crc);
754263648Sbapt	*crc_p = crc;
755262395Sbapt}
756262395Sbapt
757262395Sbapt
758262395Sbapt/*
759262395Sbapt * if frame tail not needed (incorrect number or received twice),
760263648Sbapt * it won't store, but CRC will be calculated
761262395Sbapt */
762262395Sbapt
763262395Sbaptstatic int
764262395Sbaptskip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
765262395Sbapt{
766262395Sbapt	while (tail_len--)
767262395Sbapt		crc = CRC32(sbni_inb(sc, DAT), crc);
768262395Sbapt
769262395Sbapt	return (crc == CRC32_REMAINDER);
770262395Sbapt}
771262395Sbapt
772263648Sbapt
773263648Sbaptstatic int
774262395Sbaptcheck_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
775262395Sbapt	   u_int *ack, u_int *is_first, u_int32_t *crc_p)
776262395Sbapt{
777262395Sbapt	u_int32_t crc;
778263648Sbapt	u_char value;
779262395Sbapt
780262395Sbapt	crc = *crc_p;
781262395Sbapt	if (sbni_inb(sc, DAT) != SBNI_SIG)
782262395Sbapt		return (0);
783262395Sbapt
784262395Sbapt	value = sbni_inb(sc, DAT);
785262395Sbapt	*framelen = (u_int)value;
786262395Sbapt	crc = CRC32(value, crc);
787262395Sbapt	value = sbni_inb(sc, DAT);
788262395Sbapt	*framelen |= ((u_int)value) << 8;
789262395Sbapt	crc = CRC32(value, crc);
790262395Sbapt
791263648Sbapt	*ack = *framelen & FRAME_ACK_MASK;
792263648Sbapt	*is_first = (*framelen & FRAME_FIRST) != 0;
793262395Sbapt
794262395Sbapt	if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
795262395Sbapt		return (0);
796262395Sbapt
797262395Sbapt	value = sbni_inb(sc, DAT);
798262395Sbapt	*frameno = (u_int)value;
799262395Sbapt	crc = CRC32(value, crc);
800262395Sbapt
801262395Sbapt	crc = CRC32(sbni_inb(sc, DAT), crc);		/* reserved byte */
802262395Sbapt	*framelen -= 2;
803262395Sbapt
804262395Sbapt	*crc_p = crc;
805262395Sbapt	return (1);
806262395Sbapt}
807262395Sbapt
808262395Sbapt
809262395Sbaptstatic int
810262395Sbaptget_rx_buf(struct sbni_softc *sc)
811262395Sbapt{
812262395Sbapt	struct mbuf *m;
813262395Sbapt
814262395Sbapt	MGETHDR(m, M_DONTWAIT, MT_DATA);
815262395Sbapt	if (m == NULL) {
816262395Sbapt		if_printf(sc->ifp, "cannot allocate header mbuf\n");
817262395Sbapt		return (0);
818262395Sbapt	}
819262395Sbapt
820262395Sbapt	/*
821263648Sbapt	 * We always put the received packet in a single buffer -
822263648Sbapt	 * either with just an mbuf header or in a cluster attached
823262395Sbapt	 * to the header. The +2 is to compensate for the alignment
824262395Sbapt	 * fixup below.
825262395Sbapt	 */
826262395Sbapt	if (ETHER_MAX_LEN + 2 > MHLEN) {
827262395Sbapt		/* Attach an mbuf cluster */
828262395Sbapt		MCLGET(m, M_DONTWAIT);
829262395Sbapt		if ((m->m_flags & M_EXT) == 0) {
830262395Sbapt			m_freem(m);
831262395Sbapt			return (0);
832262395Sbapt		}
833262395Sbapt	}
834262395Sbapt	m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
835262395Sbapt
836262395Sbapt	/*
837262395Sbapt	 * The +2 is to longword align the start of the real packet.
838262395Sbapt	 * (sizeof ether_header == 14)
839262395Sbapt	 * This is important for NFS.
840262395Sbapt	 */
841262395Sbapt	m_adj(m, 2);
842262395Sbapt	sc->rx_buf_p = m;
843262395Sbapt	return (1);
844262395Sbapt}
845262395Sbapt
846262395Sbapt
847262395Sbaptstatic void
848262395Sbaptindicate_pkt(struct sbni_softc *sc)
849262395Sbapt{
850262395Sbapt	struct ifnet *ifp = sc->ifp;
851262395Sbapt	struct mbuf *m;
852262395Sbapt
853262395Sbapt	m = sc->rx_buf_p;
854262395Sbapt	m->m_pkthdr.rcvif = ifp;
855262395Sbapt	m->m_pkthdr.len   = m->m_len = sc->inppos;
856262395Sbapt
857262395Sbapt	(*ifp->if_input)(ifp, m);
858262395Sbapt	sc->rx_buf_p = NULL;
859262395Sbapt}
860262395Sbapt
861262395Sbapt/* -------------------------------------------------------------------------- */
862262395Sbapt
863262395Sbapt/*
864262395Sbapt * Routine checks periodically wire activity and regenerates marker if
865262395Sbapt * connect was inactive for a long time.
866262395Sbapt */
867262395Sbapt
868262395Sbaptstatic void
869262395Sbaptsbni_timeout(void *xsc)
870262395Sbapt{
871262395Sbapt	struct sbni_softc *sc;
872262395Sbapt	int s;
873262395Sbapt	u_char csr0;
874262395Sbapt
875262395Sbapt	sc = (struct sbni_softc *)xsc;
876262395Sbapt	s = splimp();
877262395Sbapt
878262395Sbapt	csr0 = sbni_inb(sc, CSR0);
879262395Sbapt	if (csr0 & RC_CHK) {
880262395Sbapt
881262395Sbapt		if (sc->timer_ticks) {
882262395Sbapt			if (csr0 & (RC_RDY | BU_EMP))
883262395Sbapt				/* receiving not active */
884262395Sbapt				sc->timer_ticks--;
885262395Sbapt		} else {
886262395Sbapt			sc->in_stats.timeout_number++;
887262395Sbapt			if (sc->delta_rxl)
888262395Sbapt				timeout_change_level(sc);
889262395Sbapt
890262395Sbapt			sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
891262395Sbapt			csr0 = sbni_inb(sc, CSR0);
892262395Sbapt		}
893262395Sbapt	}
894262395Sbapt
895262395Sbapt	sbni_outb(sc, CSR0, csr0 | RC_CHK);
896262395Sbapt	sc->wch = timeout(sbni_timeout, sc, hz/SBNI_HZ);
897262395Sbapt	splx(s);
898262395Sbapt}
899262395Sbapt
900262395Sbapt/* -------------------------------------------------------------------------- */
901262395Sbapt
902262395Sbaptstatic void
903262395Sbaptcard_start(struct sbni_softc *sc)
904262395Sbapt{
905262395Sbapt	sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
906262395Sbapt	sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
907262395Sbapt	sc->state |= FL_PREV_OK;
908262395Sbapt
909262395Sbapt	sc->inppos = 0;
910262395Sbapt	sc->wait_frameno = 0;
911262395Sbapt
912262395Sbapt	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
913262395Sbapt	sbni_outb(sc, CSR0, EN_INT);
914262395Sbapt}
915262395Sbapt
916262395Sbapt/* -------------------------------------------------------------------------- */
917262395Sbapt
918262395Sbapt/*
919262395Sbapt * Device timeout/watchdog routine. Entered if the device neglects to
920262395Sbapt *	generate an interrupt after a transmit has been started on it.
921262395Sbapt */
922262395Sbapt
923262395Sbaptstatic void
924262395Sbaptsbni_watchdog(struct ifnet *ifp)
925262395Sbapt{
926262395Sbapt	log(LOG_ERR, "%s: device timeout\n", ifp->if_xname);
927262395Sbapt	ifp->if_oerrors++;
928262395Sbapt}
929262395Sbapt
930262395Sbapt
931262395Sbaptstatic u_char rxl_tab[] = {
932262395Sbapt	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
933262395Sbapt	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
934262395Sbapt};
935262395Sbapt
936262395Sbapt#define SIZE_OF_TIMEOUT_RXL_TAB 4
937262395Sbaptstatic u_char timeout_rxl_tab[] = {
938262395Sbapt	0x03, 0x05, 0x08, 0x0b
939262395Sbapt};
940262395Sbapt
941262395Sbaptstatic void
942262395Sbaptset_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
943262395Sbapt{
944262395Sbapt	if (flags.fixed_rxl) {
945262395Sbapt		sc->delta_rxl = 0; /* disable receive level autodetection */
946262395Sbapt		sc->cur_rxl_index = flags.rxl;
947262395Sbapt	} else {
948262395Sbapt		sc->delta_rxl = DEF_RXL_DELTA;
949262395Sbapt		sc->cur_rxl_index = DEF_RXL;
950262395Sbapt	}
951262395Sbapt
952262395Sbapt	sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
953262395Sbapt	sc->csr1.rxl  = rxl_tab[sc->cur_rxl_index];
954262395Sbapt	sc->maxframe  = DEFAULT_FRAME_LEN;
955262395Sbapt
956262395Sbapt	/*
957262395Sbapt	 * generate Ethernet address (0x00ff01xxxxxx)
958262395Sbapt	 */
959262395Sbapt	*(u_int16_t *) sc->enaddr = htons(0x00ff);
960262395Sbapt	if (flags.mac_addr) {
961262395Sbapt		*(u_int32_t *) (sc->enaddr + 2) =
962262395Sbapt		    htonl(flags.mac_addr | 0x01000000);
963262395Sbapt	} else {
964262395Sbapt		*(u_char *) (sc->enaddr + 2) = 0x01;
965262395Sbapt		read_random(sc->enaddr + 3, 3);
966262395Sbapt	}
967262395Sbapt}
968262395Sbapt
969262395Sbapt
970262395Sbapt#ifdef SBNI_DUAL_COMPOUND
971262395Sbapt
972262395Sbaptstruct sbni_softc *
973262395Sbaptconnect_to_master(struct sbni_softc *sc)
974262395Sbapt{
975262395Sbapt	struct sbni_softc *p, *p_prev;
976262395Sbapt
977262395Sbapt	for (p = sbni_headlist, p_prev = NULL; p; p_prev = p, p = p->link) {
978262395Sbapt		if (rman_get_start(p->io_res) == rman_get_start(sc->io_res) + 4 ||
979262395Sbapt		    rman_get_start(p->io_res) == rman_get_start(sc->io_res) - 4) {
980262395Sbapt			p->slave_sc = sc;
981262395Sbapt			if (p_prev)
982262395Sbapt				p_prev->link = p->link;
983262395Sbapt			else
984262395Sbapt				sbni_headlist = p->link;
985262395Sbapt			return p;
986262395Sbapt		}
987262395Sbapt	}
988262395Sbapt
989262395Sbapt	return (NULL);
990262395Sbapt}
991262395Sbapt
992262395Sbapt#endif	/* SBNI_DUAL_COMPOUND */
993262395Sbapt
994262395Sbapt
995262395Sbapt/* Receive level auto-selection */
996262395Sbapt
997262395Sbaptstatic void
998262395Sbaptchange_level(struct sbni_softc *sc)
999262395Sbapt{
1000262395Sbapt	if (sc->delta_rxl == 0)		/* do not auto-negotiate RxL */
1001262395Sbapt		return;
1002262395Sbapt
1003262395Sbapt	if (sc->cur_rxl_index == 0)
1004262395Sbapt		sc->delta_rxl = 1;
1005262395Sbapt	else if (sc->cur_rxl_index == 15)
1006262395Sbapt		sc->delta_rxl = -1;
1007262395Sbapt	else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
1008262395Sbapt		sc->delta_rxl = -sc->delta_rxl;
1009262395Sbapt
1010262395Sbapt	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
1011262395Sbapt	sbni_inb(sc, CSR0);	/* it needed for PCI cards */
1012262395Sbapt	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
1013262395Sbapt
1014262395Sbapt	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
1015262395Sbapt	sc->cur_rxl_rcvd  = 0;
1016262395Sbapt}
1017262395Sbapt
1018262395Sbapt
1019262395Sbaptstatic void
1020262395Sbapttimeout_change_level(struct sbni_softc *sc)
1021262395Sbapt{
1022262395Sbapt	sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
1023262395Sbapt	if (++sc->timeout_rxl >= 4)
1024262395Sbapt		sc->timeout_rxl = 0;
1025262395Sbapt
1026262395Sbapt	sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
1027262395Sbapt	sbni_inb(sc, CSR0);
1028262395Sbapt	sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
1029262395Sbapt
1030262395Sbapt	sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
1031262395Sbapt	sc->cur_rxl_rcvd  = 0;
1032262395Sbapt}
1033262395Sbapt
1034262395Sbapt/* -------------------------------------------------------------------------- */
1035262395Sbapt
1036262395Sbapt/*
1037262395Sbapt * Process an ioctl request. This code needs some work - it looks
1038262395Sbapt *	pretty ugly.
1039262395Sbapt */
1040262395Sbapt
1041262395Sbaptstatic int
1042262395Sbaptsbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
1043262395Sbapt{
1044262395Sbapt	struct sbni_softc *sc;
1045262395Sbapt	struct ifreq *ifr;
1046262395Sbapt	struct thread *td;
1047262395Sbapt	struct sbni_in_stats *in_stats;
1048262395Sbapt	struct sbni_flags flags;
1049262395Sbapt	int error, s;
1050262395Sbapt
1051262395Sbapt	sc = ifp->if_softc;
1052262395Sbapt	ifr = (struct ifreq *)data;
1053262395Sbapt	td = curthread;
1054262395Sbapt	error = 0;
1055262395Sbapt
1056262395Sbapt	s = splimp();
1057262395Sbapt
1058262395Sbapt	switch (command) {
1059262395Sbapt	case SIOCSIFFLAGS:
1060262395Sbapt		/*
1061262395Sbapt		 * If the interface is marked up and stopped, then start it.
1062262395Sbapt		 * If it is marked down and running, then stop it.
1063262395Sbapt		 */
1064262395Sbapt		if (ifp->if_flags & IFF_UP) {
1065262395Sbapt			if (!(ifp->if_flags & IFF_RUNNING))
1066262395Sbapt				sbni_init(sc);
1067262395Sbapt		} else {
1068262395Sbapt			if (ifp->if_flags & IFF_RUNNING) {
1069262395Sbapt				sbni_stop(sc);
1070262395Sbapt				ifp->if_flags &= ~IFF_RUNNING;
1071262395Sbapt			}
1072262395Sbapt		}
1073262395Sbapt		break;
1074262395Sbapt
1075262395Sbapt	case SIOCADDMULTI:
1076262395Sbapt	case SIOCDELMULTI:
1077262395Sbapt		/*
1078262395Sbapt		 * Multicast list has changed; set the hardware filter
1079262395Sbapt		 * accordingly.
1080262395Sbapt		 */
1081262395Sbapt		error = 0;
1082262395Sbapt		/* if (ifr == NULL)
1083262395Sbapt			error = EAFNOSUPPORT; */
1084262395Sbapt		break;
1085262395Sbapt
1086262395Sbapt	case SIOCSIFMTU:
1087262395Sbapt		if (ifr->ifr_mtu > ETHERMTU)
1088262395Sbapt			error = EINVAL;
1089262395Sbapt		else
1090262395Sbapt			ifp->if_mtu = ifr->ifr_mtu;
1091262395Sbapt		break;
1092262395Sbapt
1093262395Sbapt		/*
1094262395Sbapt		 * SBNI specific ioctl
1095262395Sbapt		 */
1096264789Sbapt	case SIOCGHWFLAGS:	/* get flags */
1097262395Sbapt		bcopy((caddr_t)IFP2ENADDR(sc->ifp)+3, (caddr_t) &flags, 3);
1098262395Sbapt		flags.rxl = sc->cur_rxl_index;
1099262395Sbapt		flags.rate = sc->csr1.rate;
1100262395Sbapt		flags.fixed_rxl = (sc->delta_rxl == 0);
1101264789Sbapt		flags.fixed_rate = 1;
1102262395Sbapt		ifr->ifr_data = *(caddr_t*) &flags;
1103262395Sbapt		break;
1104262395Sbapt
1105262395Sbapt	case SIOCGINSTATS:
1106262395Sbapt		in_stats = (struct sbni_in_stats *)ifr->ifr_data;
1107262395Sbapt		bcopy((void *)(&(sc->in_stats)), (void *)in_stats,
1108264789Sbapt		      sizeof(struct sbni_in_stats));
1109262395Sbapt		break;
1110262395Sbapt
1111262395Sbapt	case SIOCSHWFLAGS:	/* set flags */
1112262395Sbapt		/* root only */
1113262395Sbapt		error = suser(td);
1114262395Sbapt		if (error)
1115262395Sbapt			break;
1116262395Sbapt		flags = *(struct sbni_flags*)&ifr->ifr_data;
1117262395Sbapt		if (flags.fixed_rxl) {
1118262395Sbapt			sc->delta_rxl = 0;
1119262395Sbapt			sc->cur_rxl_index = flags.rxl;
1120262395Sbapt		} else {
1121262395Sbapt			sc->delta_rxl = DEF_RXL_DELTA;
1122262395Sbapt			sc->cur_rxl_index = DEF_RXL;
1123262395Sbapt		}
1124262395Sbapt		sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
1125262395Sbapt		sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
1126262395Sbapt		if (flags.mac_addr)
1127262395Sbapt			bcopy((caddr_t) &flags,
1128262395Sbapt			      (caddr_t) IFP2ENADDR(sc->ifp)+3, 3);
1129262395Sbapt
1130262395Sbapt		/* Don't be afraid... */
1131262395Sbapt		sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
1132262395Sbapt		break;
1133262395Sbapt
1134262395Sbapt	case SIOCRINSTATS:
1135262395Sbapt		if (!(error = suser(td)))	/* root only */
1136262395Sbapt			bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
1137262395Sbapt		break;
1138262395Sbapt
1139262395Sbapt	default:
1140262395Sbapt		error = ether_ioctl(ifp, command, data);
1141262395Sbapt		break;
1142262395Sbapt	}
1143262395Sbapt
1144262395Sbapt	splx(s);
1145262395Sbapt	return (error);
1146262395Sbapt}
1147262395Sbapt
1148262395Sbapt/* -------------------------------------------------------------------------- */
1149262395Sbapt
1150262395Sbapt#ifdef ASM_CRC
1151262395Sbapt
1152262395Sbaptstatic u_int32_t
1153262395Sbaptcalc_crc32(u_int32_t crc, caddr_t p, u_int len)
1154262395Sbapt{
1155262395Sbapt	register u_int32_t  _crc __asm ("ax");
1156262395Sbapt	_crc = crc;
1157262395Sbapt
1158262395Sbapt	__asm __volatile (
1159262395Sbapt		"xorl	%%ebx, %%ebx\n"
1160262395Sbapt		"movl	%1, %%esi\n"
1161262395Sbapt		"movl	%2, %%ecx\n"
1162262395Sbapt		"movl	$crc32tab, %%edi\n"
1163262395Sbapt		"shrl	$2, %%ecx\n"
1164262395Sbapt		"jz	1f\n"
1165262395Sbapt
1166262395Sbapt		".align 4\n"
1167262395Sbapt	"0:\n"
1168262395Sbapt		"movb	%%al, %%bl\n"
1169262395Sbapt		"movl	(%%esi), %%edx\n"
1170262395Sbapt		"shrl	$8, %%eax\n"
1171262395Sbapt		"xorb	%%dl, %%bl\n"
1172262395Sbapt		"shrl	$8, %%edx\n"
1173262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1174262395Sbapt
1175262395Sbapt		"movb	%%al, %%bl\n"
1176262395Sbapt		"shrl	$8, %%eax\n"
1177262395Sbapt		"xorb	%%dl, %%bl\n"
1178262395Sbapt		"shrl	$8, %%edx\n"
1179262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1180262395Sbapt
1181262395Sbapt		"movb	%%al, %%bl\n"
1182262395Sbapt		"shrl	$8, %%eax\n"
1183262395Sbapt		"xorb	%%dl, %%bl\n"
1184262395Sbapt		"movb	%%dh, %%dl\n"
1185262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1186262395Sbapt
1187262395Sbapt		"movb	%%al, %%bl\n"
1188262395Sbapt		"shrl	$8, %%eax\n"
1189262395Sbapt		"xorb	%%dl, %%bl\n"
1190262395Sbapt		"addl	$4, %%esi\n"
1191262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1192262395Sbapt
1193262395Sbapt		"decl	%%ecx\n"
1194262395Sbapt		"jnz	0b\n"
1195262395Sbapt
1196262395Sbapt	"1:\n"
1197262395Sbapt		"movl	%2, %%ecx\n"
1198262395Sbapt		"andl	$3, %%ecx\n"
1199262395Sbapt		"jz	2f\n"
1200262395Sbapt
1201262395Sbapt		"movb	%%al, %%bl\n"
1202262395Sbapt		"shrl	$8, %%eax\n"
1203262395Sbapt		"xorb	(%%esi), %%bl\n"
1204262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1205262395Sbapt
1206262395Sbapt		"decl	%%ecx\n"
1207262395Sbapt		"jz	2f\n"
1208262395Sbapt
1209262395Sbapt		"movb	%%al, %%bl\n"
1210262395Sbapt		"shrl	$8, %%eax\n"
1211262395Sbapt		"xorb	1(%%esi), %%bl\n"
1212262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1213262395Sbapt
1214262395Sbapt		"decl	%%ecx\n"
1215262395Sbapt		"jz	2f\n"
1216262395Sbapt
1217262395Sbapt		"movb	%%al, %%bl\n"
1218262395Sbapt		"shrl	$8, %%eax\n"
1219262395Sbapt		"xorb	2(%%esi), %%bl\n"
1220262395Sbapt		"xorl	(%%edi,%%ebx,4), %%eax\n"
1221262395Sbapt	"2:\n"
1222262395Sbapt		: "=a" (_crc)
1223262395Sbapt		: "g" (p), "g" (len)
1224262395Sbapt		: "bx", "cx", "dx", "si", "di"
1225262395Sbapt	);
1226262395Sbapt
1227262395Sbapt	return (_crc);
1228262395Sbapt}
1229262395Sbapt
1230262395Sbapt#else	/* ASM_CRC */
1231262395Sbapt
1232262395Sbaptstatic u_int32_t
1233262395Sbaptcalc_crc32(u_int32_t crc, caddr_t p, u_int len)
1234262395Sbapt{
1235262395Sbapt	while (len--)
1236262395Sbapt		crc = CRC32(*p++, crc);
1237262395Sbapt
1238262395Sbapt	return (crc);
1239262395Sbapt}
1240262395Sbapt
1241262395Sbapt#endif	/* ASM_CRC */
1242262395Sbapt
1243262395Sbapt
1244262395Sbaptstatic u_int32_t crc32tab[] __aligned(8) = {
1245262395Sbapt	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37,
1246262395Sbapt	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E,
1247262395Sbapt	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605,
1248262395Sbapt	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C,
1249262395Sbapt	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53,
1250262395Sbapt	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A,
1251262395Sbapt	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661,
1252262975Sbapt	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278,
1253262975Sbapt	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF,
1254262975Sbapt	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6,
1255262975Sbapt	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD,
1256262975Sbapt	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4,
1257262975Sbapt	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B,
1258262975Sbapt	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82,
1259262975Sbapt	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9,
1260262975Sbapt	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0,
1261262975Sbapt	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7,
1262262975Sbapt	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE,
1263262975Sbapt	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795,
1264262975Sbapt	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C,
1265262975Sbapt	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3,
1266262975Sbapt	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA,
1267262975Sbapt	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1,
1268262975Sbapt	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8,
1269262975Sbapt	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F,
1270262975Sbapt	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76,
1271262975Sbapt	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D,
1272262975Sbapt	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344,
1273262975Sbapt	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B,
1274262395Sbapt	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12,
1275262395Sbapt	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739,
1276262395Sbapt	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320,
1277262395Sbapt	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17,
1278262395Sbapt	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E,
1279262395Sbapt	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525,
1280262395Sbapt	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C,
1281262395Sbapt	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73,
1282262395Sbapt	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A,
1283262395Sbapt	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541,
1284262975Sbapt	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158,
1285262395Sbapt	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF,
1286262395Sbapt	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6,
1287262395Sbapt	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED,
1288262395Sbapt	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4,
1289262395Sbapt	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB,
1290262395Sbapt	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2,
1291262975Sbapt	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589,
1292262975Sbapt	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190,
1293262975Sbapt	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87,
1294262975Sbapt	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E,
1295262975Sbapt	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5,
1296262975Sbapt	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC,
1297262975Sbapt	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3,
1298262975Sbapt	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA,
1299262975Sbapt	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1,
1300262975Sbapt	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8,
1301262975Sbapt	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F,
1302262975Sbapt	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856,
1303262395Sbapt	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D,
1304262395Sbapt	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064,
1305262395Sbapt	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B,
1306262395Sbapt	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832,
1307262975Sbapt	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419,
1308262395Sbapt	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000
1309262395Sbapt};
1310262395Sbapt