2008-09-10 18:36:58 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 1997-2001 Granch, Ltd. All rights reserved.
|
|
|
|
* Author: Denis I.Timofeev <timofeev@granch.ru>
|
|
|
|
*
|
|
|
|
* Redistributon and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice unmodified, this list of conditions, and the following
|
|
|
|
* disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Device driver for Granch SBNI12 leased line adapters
|
|
|
|
*
|
|
|
|
* Revision 2.0.0 1997/08/06
|
|
|
|
* Initial revision by Alexey Zverev
|
|
|
|
*
|
|
|
|
* Revision 2.0.1 1997/08/11
|
|
|
|
* Additional internal statistics support (tx statistics)
|
|
|
|
*
|
|
|
|
* Revision 2.0.2 1997/11/05
|
|
|
|
* if_bpf bug has been fixed
|
|
|
|
*
|
|
|
|
* Revision 2.0.3 1998/12/20
|
|
|
|
* Memory leakage has been eliminated in
|
|
|
|
* the sbni_st and sbni_timeout routines.
|
|
|
|
*
|
|
|
|
* Revision 3.0 2000/08/10 by Yaroslav Polyakov
|
|
|
|
* Support for PCI cards. 4.1 modification.
|
|
|
|
*
|
|
|
|
* Revision 3.1 2000/09/12
|
|
|
|
* Removed extra #defines around bpf functions
|
|
|
|
*
|
|
|
|
* Revision 4.0 2000/11/23 by Denis Timofeev
|
|
|
|
* Completely redesigned the buffer management
|
|
|
|
*
|
|
|
|
* Revision 4.1 2001/01/21
|
|
|
|
* Support for PCI Dual cards and new SBNI12D-10, -11 Dual/ISA cards
|
|
|
|
*
|
|
|
|
* Written with reference to NE2000 driver developed by David Greenman.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/priv.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/callout.h>
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/random.h>
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <sys/rman.h>
|
|
|
|
#include <machine/resource.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include <net/if_types.h>
|
|
|
|
|
|
|
|
#include <dev/sbni/if_sbnireg.h>
|
|
|
|
#include <dev/sbni/if_sbnivar.h>
|
|
|
|
|
|
|
|
static void sbni_init(void *);
|
|
|
|
static void sbni_init_locked(struct sbni_softc *);
|
|
|
|
static void sbni_start(struct ifnet *);
|
|
|
|
static void sbni_start_locked(struct ifnet *);
|
|
|
|
static int sbni_ioctl(struct ifnet *, u_long, caddr_t);
|
|
|
|
static void sbni_stop(struct sbni_softc *);
|
|
|
|
static void handle_channel(struct sbni_softc *);
|
|
|
|
|
|
|
|
static void card_start(struct sbni_softc *);
|
|
|
|
static int recv_frame(struct sbni_softc *);
|
|
|
|
static void send_frame(struct sbni_softc *);
|
|
|
|
static int upload_data(struct sbni_softc *, u_int, u_int, u_int, u_int32_t);
|
|
|
|
static int skip_tail(struct sbni_softc *, u_int, u_int32_t);
|
|
|
|
static void interpret_ack(struct sbni_softc *, u_int);
|
|
|
|
static void download_data(struct sbni_softc *, u_int32_t *);
|
|
|
|
static void prepare_to_send(struct sbni_softc *);
|
|
|
|
static void drop_xmit_queue(struct sbni_softc *);
|
|
|
|
static int get_rx_buf(struct sbni_softc *);
|
|
|
|
static void indicate_pkt(struct sbni_softc *);
|
|
|
|
static void change_level(struct sbni_softc *);
|
|
|
|
static int check_fhdr(struct sbni_softc *, u_int *, u_int *,
|
|
|
|
u_int *, u_int *, u_int32_t *);
|
|
|
|
static int append_frame_to_pkt(struct sbni_softc *, u_int, u_int32_t);
|
|
|
|
static void timeout_change_level(struct sbni_softc *);
|
|
|
|
static void send_frame_header(struct sbni_softc *, u_int32_t *);
|
|
|
|
static void set_initial_values(struct sbni_softc *, struct sbni_flags);
|
|
|
|
|
|
|
|
static u_int32_t calc_crc32(u_int32_t, caddr_t, u_int);
|
|
|
|
static timeout_t sbni_timeout;
|
|
|
|
|
|
|
|
static __inline u_char sbni_inb(struct sbni_softc *, enum sbni_reg);
|
|
|
|
static __inline void sbni_outb(struct sbni_softc *, enum sbni_reg, u_char);
|
|
|
|
static __inline void sbni_insb(struct sbni_softc *, u_char *, u_int);
|
|
|
|
static __inline void sbni_outsb(struct sbni_softc *, u_char *, u_int);
|
|
|
|
|
|
|
|
static u_int32_t crc32tab[];
|
|
|
|
|
|
|
|
#ifdef SBNI_DUAL_COMPOUND
|
|
|
|
static struct mtx headlist_lock;
|
|
|
|
MTX_SYSINIT(headlist_lock, &headlist_lock, "sbni headlist", MTX_DEF);
|
|
|
|
static struct sbni_softc *sbni_headlist;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static __inline u_char
|
|
|
|
sbni_inb(struct sbni_softc *sc, enum sbni_reg reg)
|
|
|
|
{
|
|
|
|
return bus_space_read_1(
|
|
|
|
rman_get_bustag(sc->io_res),
|
|
|
|
rman_get_bushandle(sc->io_res),
|
|
|
|
sc->io_off + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline void
|
|
|
|
sbni_outb(struct sbni_softc *sc, enum sbni_reg reg, u_char value)
|
|
|
|
{
|
|
|
|
bus_space_write_1(
|
|
|
|
rman_get_bustag(sc->io_res),
|
|
|
|
rman_get_bushandle(sc->io_res),
|
|
|
|
sc->io_off + reg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline void
|
|
|
|
sbni_insb(struct sbni_softc *sc, u_char *to, u_int len)
|
|
|
|
{
|
|
|
|
bus_space_read_multi_1(
|
|
|
|
rman_get_bustag(sc->io_res),
|
|
|
|
rman_get_bushandle(sc->io_res),
|
|
|
|
sc->io_off + DAT, to, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline void
|
|
|
|
sbni_outsb(struct sbni_softc *sc, u_char *from, u_int len)
|
|
|
|
{
|
|
|
|
bus_space_write_multi_1(
|
|
|
|
rman_get_bustag(sc->io_res),
|
|
|
|
rman_get_bushandle(sc->io_res),
|
|
|
|
sc->io_off + DAT, from, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Valid combinations in CSR0 (for probing):
|
|
|
|
|
|
|
|
VALID_DECODER 0000,0011,1011,1010
|
|
|
|
|
|
|
|
; 0 ; -
|
|
|
|
TR_REQ ; 1 ; +
|
|
|
|
TR_RDY ; 2 ; -
|
|
|
|
TR_RDY TR_REQ ; 3 ; +
|
|
|
|
BU_EMP ; 4 ; +
|
|
|
|
BU_EMP TR_REQ ; 5 ; +
|
|
|
|
BU_EMP TR_RDY ; 6 ; -
|
|
|
|
BU_EMP TR_RDY TR_REQ ; 7 ; +
|
|
|
|
RC_RDY ; 8 ; +
|
|
|
|
RC_RDY TR_REQ ; 9 ; +
|
|
|
|
RC_RDY TR_RDY ; 10 ; -
|
|
|
|
RC_RDY TR_RDY TR_REQ ; 11 ; -
|
|
|
|
RC_RDY BU_EMP ; 12 ; -
|
|
|
|
RC_RDY BU_EMP TR_REQ ; 13 ; -
|
|
|
|
RC_RDY BU_EMP TR_RDY ; 14 ; -
|
|
|
|
RC_RDY BU_EMP TR_RDY TR_REQ ; 15 ; -
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define VALID_DECODER (2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
sbni_probe(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
u_char csr0;
|
|
|
|
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
if (csr0 != 0xff && csr0 != 0x00) {
|
|
|
|
csr0 &= ~EN_INT;
|
|
|
|
if (csr0 & BU_EMP)
|
|
|
|
csr0 |= EN_INT;
|
|
|
|
|
|
|
|
if (VALID_DECODER & (1 << (csr0 >> 4)))
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Install interface into kernel networking data structures
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
sbni_attach(struct sbni_softc *sc, int unit, struct sbni_flags flags)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
u_char csr0;
|
|
|
|
|
|
|
|
ifp = sc->ifp = if_alloc(IFT_ETHER);
|
|
|
|
if (ifp == NULL)
|
|
|
|
return (ENOMEM);
|
|
|
|
sbni_outb(sc, CSR0, 0);
|
|
|
|
set_initial_values(sc, flags);
|
|
|
|
|
|
|
|
/* Initialize ifnet structure */
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
if_initname(ifp, "sbni", unit);
|
|
|
|
ifp->if_init = sbni_init;
|
|
|
|
ifp->if_start = sbni_start;
|
|
|
|
ifp->if_ioctl = sbni_ioctl;
|
2010-05-03 07:32:50 +00:00
|
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
|
2008-09-10 18:36:58 +00:00
|
|
|
|
|
|
|
/* report real baud rate */
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
ifp->if_baudrate =
|
|
|
|
(csr0 & 0x01 ? 500000 : 2000000) / (1 << flags.rate);
|
|
|
|
|
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
|
|
|
|
mtx_init(&sc->lock, ifp->if_xname, MTX_NETWORK_LOCK, MTX_DEF);
|
|
|
|
callout_init_mtx(&sc->wch, &sc->lock, 0);
|
|
|
|
ether_ifattach(ifp, sc->enaddr);
|
|
|
|
/* device attach does transition from UNCONFIGURED to IDLE state */
|
|
|
|
|
|
|
|
if_printf(ifp, "speed %ld, rxl ", ifp->if_baudrate);
|
|
|
|
if (sc->delta_rxl)
|
|
|
|
printf("auto\n");
|
|
|
|
else
|
|
|
|
printf("%d (fixed)\n", sc->cur_rxl_index);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sbni_detach(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
sbni_stop(sc);
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
callout_drain(&sc->wch);
|
|
|
|
ether_ifdetach(sc->ifp);
|
|
|
|
if (sc->irq_handle)
|
|
|
|
bus_teardown_intr(sc->dev, sc->irq_res, sc->irq_handle);
|
|
|
|
mtx_destroy(&sc->lock);
|
|
|
|
if_free(sc->ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
sbni_release_resources(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (sc->irq_res)
|
|
|
|
bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irq_rid,
|
|
|
|
sc->irq_res);
|
|
|
|
if (sc->io_res && sc->io_off == 0)
|
|
|
|
bus_release_resource(sc->dev, SYS_RES_IOPORT, sc->io_rid,
|
|
|
|
sc->io_res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void
|
|
|
|
sbni_init(void *xsc)
|
|
|
|
{
|
|
|
|
struct sbni_softc *sc;
|
|
|
|
|
|
|
|
sc = (struct sbni_softc *)xsc;
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
sbni_init_locked(sc);
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sbni_init_locked(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
ifp = sc->ifp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* kludge to avoid multiple initialization when more than once
|
|
|
|
* protocols configured
|
|
|
|
*/
|
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
card_start(sc);
|
|
|
|
callout_reset(&sc->wch, hz/SBNI_HZ, sbni_timeout, sc);
|
|
|
|
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
|
|
|
|
|
|
/* attempt to start output */
|
|
|
|
sbni_start_locked(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sbni_start(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct sbni_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
sbni_start_locked(ifp);
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
sbni_start_locked(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct sbni_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
if (sc->tx_frameno == 0)
|
|
|
|
prepare_to_send(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
sbni_stop(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
sbni_outb(sc, CSR0, 0);
|
|
|
|
drop_xmit_queue(sc);
|
|
|
|
|
|
|
|
if (sc->rx_buf_p) {
|
|
|
|
m_freem(sc->rx_buf_p);
|
|
|
|
sc->rx_buf_p = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
callout_stop(&sc->wch);
|
|
|
|
sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* interrupt handler */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SBNI12D-10, -11/ISA boards within "common interrupt" mode could not
|
|
|
|
* be looked as two independent single-channel devices. Every channel seems
|
|
|
|
* as Ethernet interface but interrupt handler must be common. Really, first
|
|
|
|
* channel ("master") driver only registers the handler. In it's struct softc
|
|
|
|
* it has got pointer to "slave" channel's struct softc and handles that's
|
|
|
|
* interrupts too.
|
|
|
|
* softc of successfully attached ISA SBNI boards is linked to list.
|
|
|
|
* While next board driver is initialized, it scans this list. If one
|
|
|
|
* has found softc with same irq and ioaddr different by 4 then it assumes
|
|
|
|
* this board to be "master".
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
sbni_intr(void *arg)
|
|
|
|
{
|
|
|
|
struct sbni_softc *sc;
|
|
|
|
int repeat;
|
|
|
|
|
|
|
|
sc = (struct sbni_softc *)arg;
|
|
|
|
|
|
|
|
do {
|
|
|
|
repeat = 0;
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
if (sbni_inb(sc, CSR0) & (RC_RDY | TR_RDY)) {
|
|
|
|
handle_channel(sc);
|
|
|
|
repeat = 1;
|
|
|
|
}
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
if (sc->slave_sc) {
|
|
|
|
/* second channel present */
|
|
|
|
SBNI_LOCK(sc->slave_sc);
|
|
|
|
if (sbni_inb(sc->slave_sc, CSR0) & (RC_RDY | TR_RDY)) {
|
|
|
|
handle_channel(sc->slave_sc);
|
|
|
|
repeat = 1;
|
|
|
|
}
|
|
|
|
SBNI_UNLOCK(sc->slave_sc);
|
|
|
|
}
|
|
|
|
} while (repeat);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_channel(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
int req_ans;
|
|
|
|
u_char csr0;
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR0, (sbni_inb(sc, CSR0) & ~EN_INT) | TR_REQ);
|
|
|
|
|
|
|
|
sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
|
|
|
|
for (;;) {
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
if ((csr0 & (RC_RDY | TR_RDY)) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
req_ans = !(sc->state & FL_PREV_OK);
|
|
|
|
|
|
|
|
if (csr0 & RC_RDY)
|
|
|
|
req_ans = recv_frame(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TR_RDY always equals 1 here because we have owned the marker,
|
|
|
|
* and we set TR_REQ when disabled interrupts
|
|
|
|
*/
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
if ((csr0 & TR_RDY) == 0 || (csr0 & RC_RDY) != 0)
|
|
|
|
if_printf(sc->ifp, "internal error!\n");
|
|
|
|
|
|
|
|
/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */
|
|
|
|
if (req_ans || sc->tx_frameno != 0)
|
|
|
|
send_frame(sc);
|
|
|
|
else {
|
|
|
|
/* send the marker without any data */
|
|
|
|
sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) & ~TR_REQ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | EN_INT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routine returns 1 if it need to acknoweledge received frame.
|
|
|
|
* Empty frame received without errors won't be acknoweledged.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
recv_frame(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
u_int32_t crc;
|
|
|
|
u_int framelen, frameno, ack;
|
|
|
|
u_int is_first, frame_ok;
|
|
|
|
|
|
|
|
crc = CRC32_INITIAL;
|
|
|
|
if (check_fhdr(sc, &framelen, &frameno, &ack, &is_first, &crc)) {
|
|
|
|
frame_ok = framelen > 4 ?
|
|
|
|
upload_data(sc, framelen, frameno, is_first, crc) :
|
|
|
|
skip_tail(sc, framelen, crc);
|
|
|
|
if (frame_ok)
|
|
|
|
interpret_ack(sc, ack);
|
|
|
|
} else {
|
|
|
|
framelen = 0;
|
|
|
|
frame_ok = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) ^ CT_ZER);
|
|
|
|
if (frame_ok) {
|
|
|
|
sc->state |= FL_PREV_OK;
|
|
|
|
if (framelen > 4)
|
|
|
|
sc->in_stats.all_rx_number++;
|
|
|
|
} else {
|
|
|
|
sc->state &= ~FL_PREV_OK;
|
|
|
|
change_level(sc);
|
|
|
|
sc->in_stats.all_rx_number++;
|
|
|
|
sc->in_stats.bad_rx_number++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (!frame_ok || framelen > 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
send_frame(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
u_int32_t crc;
|
|
|
|
u_char csr0;
|
|
|
|
|
|
|
|
crc = CRC32_INITIAL;
|
|
|
|
if (sc->state & FL_NEED_RESEND) {
|
|
|
|
|
|
|
|
/* if frame was sended but not ACK'ed - resend it */
|
|
|
|
if (sc->trans_errors) {
|
|
|
|
sc->trans_errors--;
|
|
|
|
if (sc->framelen != 0)
|
|
|
|
sc->in_stats.resend_tx_number++;
|
|
|
|
} else {
|
|
|
|
/* cannot xmit with many attempts */
|
|
|
|
drop_xmit_queue(sc);
|
|
|
|
goto do_send;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sc->trans_errors = TR_ERROR_COUNT;
|
|
|
|
|
|
|
|
send_frame_header(sc, &crc);
|
|
|
|
sc->state |= FL_NEED_RESEND;
|
|
|
|
/*
|
|
|
|
* FL_NEED_RESEND will be cleared after ACK, but if empty
|
|
|
|
* frame sended then in prepare_to_send next frame
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (sc->framelen) {
|
|
|
|
download_data(sc, &crc);
|
|
|
|
sc->in_stats.all_tx_number++;
|
|
|
|
sc->state |= FL_WAIT_ACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
sbni_outsb(sc, (u_char *)&crc, sizeof crc);
|
|
|
|
|
|
|
|
do_send:
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
sbni_outb(sc, CSR0, csr0 & ~TR_REQ);
|
|
|
|
|
|
|
|
if (sc->tx_frameno) {
|
|
|
|
/* next frame exists - request to send */
|
|
|
|
sbni_outb(sc, CSR0, csr0 | TR_REQ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
download_data(struct sbni_softc *sc, u_int32_t *crc_p)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
caddr_t data_p;
|
|
|
|
u_int data_len, pos, slice;
|
|
|
|
|
|
|
|
data_p = NULL; /* initialized to avoid warn */
|
|
|
|
pos = 0;
|
|
|
|
|
|
|
|
for (m = sc->tx_buf_p; m != NULL && pos < sc->pktlen; m = m->m_next) {
|
|
|
|
if (pos + m->m_len > sc->outpos) {
|
|
|
|
data_len = m->m_len - (sc->outpos - pos);
|
|
|
|
data_p = mtod(m, caddr_t) + (sc->outpos - pos);
|
|
|
|
|
|
|
|
goto do_copy;
|
|
|
|
} else
|
|
|
|
pos += m->m_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
data_len = 0;
|
|
|
|
|
|
|
|
do_copy:
|
|
|
|
pos = 0;
|
|
|
|
do {
|
|
|
|
if (data_len) {
|
|
|
|
slice = min(data_len, sc->framelen - pos);
|
|
|
|
sbni_outsb(sc, data_p, slice);
|
|
|
|
*crc_p = calc_crc32(*crc_p, data_p, slice);
|
|
|
|
|
|
|
|
pos += slice;
|
|
|
|
if (data_len -= slice)
|
|
|
|
data_p += slice;
|
|
|
|
else {
|
|
|
|
do {
|
|
|
|
m = m->m_next;
|
|
|
|
} while (m != NULL && m->m_len == 0);
|
|
|
|
|
|
|
|
if (m) {
|
|
|
|
data_len = m->m_len;
|
|
|
|
data_p = mtod(m, caddr_t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* frame too short - zero padding */
|
|
|
|
|
|
|
|
pos = sc->framelen - pos;
|
|
|
|
while (pos--) {
|
|
|
|
sbni_outb(sc, DAT, 0);
|
|
|
|
*crc_p = CRC32(0, *crc_p);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} while (pos < sc->framelen);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
upload_data(struct sbni_softc *sc, u_int framelen, u_int frameno,
|
|
|
|
u_int is_first, u_int32_t crc)
|
|
|
|
{
|
|
|
|
int frame_ok;
|
|
|
|
|
|
|
|
if (is_first) {
|
|
|
|
sc->wait_frameno = frameno;
|
|
|
|
sc->inppos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->wait_frameno == frameno) {
|
|
|
|
|
|
|
|
if (sc->inppos + framelen <= ETHER_MAX_LEN) {
|
|
|
|
frame_ok = append_frame_to_pkt(sc, framelen, crc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if CRC is right but framelen incorrect then transmitter
|
|
|
|
* error was occured... drop entire packet
|
|
|
|
*/
|
|
|
|
} else if ((frame_ok = skip_tail(sc, framelen, crc)) != 0) {
|
|
|
|
sc->wait_frameno = 0;
|
|
|
|
sc->inppos = 0;
|
|
|
|
sc->ifp->if_ierrors++;
|
|
|
|
/* now skip all frames until is_first != 0 */
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
frame_ok = skip_tail(sc, framelen, crc);
|
|
|
|
|
|
|
|
if (is_first && !frame_ok) {
|
|
|
|
/*
|
|
|
|
* Frame has been violated, but we have stored
|
|
|
|
* is_first already... Drop entire packet.
|
|
|
|
*/
|
|
|
|
sc->wait_frameno = 0;
|
|
|
|
sc->ifp->if_ierrors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (frame_ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static __inline void send_complete(struct sbni_softc *);
|
|
|
|
|
|
|
|
static __inline void
|
|
|
|
send_complete(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
m_freem(sc->tx_buf_p);
|
|
|
|
sc->tx_buf_p = NULL;
|
|
|
|
sc->ifp->if_opackets++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
interpret_ack(struct sbni_softc *sc, u_int ack)
|
|
|
|
{
|
|
|
|
if (ack == FRAME_SENT_OK) {
|
|
|
|
sc->state &= ~FL_NEED_RESEND;
|
|
|
|
|
|
|
|
if (sc->state & FL_WAIT_ACK) {
|
|
|
|
sc->outpos += sc->framelen;
|
|
|
|
|
|
|
|
if (--sc->tx_frameno) {
|
|
|
|
sc->framelen = min(
|
|
|
|
sc->maxframe, sc->pktlen - sc->outpos);
|
|
|
|
} else {
|
|
|
|
send_complete(sc);
|
|
|
|
prepare_to_send(sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->state &= ~FL_WAIT_ACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Glue received frame with previous fragments of packet.
|
|
|
|
* Indicate packet when last frame would be accepted.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
append_frame_to_pkt(struct sbni_softc *sc, u_int framelen, u_int32_t crc)
|
|
|
|
{
|
|
|
|
caddr_t p;
|
|
|
|
|
|
|
|
if (sc->inppos + framelen > ETHER_MAX_LEN)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (!sc->rx_buf_p && !get_rx_buf(sc))
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
p = sc->rx_buf_p->m_data + sc->inppos;
|
|
|
|
sbni_insb(sc, p, framelen);
|
|
|
|
if (calc_crc32(crc, p, framelen) != CRC32_REMAINDER)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
sc->inppos += framelen - 4;
|
|
|
|
if (--sc->wait_frameno == 0) { /* last frame received */
|
|
|
|
indicate_pkt(sc);
|
|
|
|
sc->ifp->if_ipackets++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare to start output on adapter. Current priority must be set to splimp
|
|
|
|
* before this routine is called.
|
|
|
|
* Transmitter will be actually activated when marker has been accepted.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
prepare_to_send(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
u_int len;
|
|
|
|
|
|
|
|
/* sc->tx_buf_p == NULL here! */
|
|
|
|
if (sc->tx_buf_p)
|
|
|
|
printf("sbni: memory leak!\n");
|
|
|
|
|
|
|
|
sc->outpos = 0;
|
|
|
|
sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
IF_DEQUEUE(&sc->ifp->if_snd, sc->tx_buf_p);
|
|
|
|
if (!sc->tx_buf_p) {
|
|
|
|
/* nothing to transmit... */
|
|
|
|
sc->pktlen = 0;
|
|
|
|
sc->tx_frameno = 0;
|
|
|
|
sc->framelen = 0;
|
|
|
|
sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (len = 0, m = sc->tx_buf_p; m; m = m->m_next)
|
|
|
|
len += m->m_len;
|
|
|
|
|
|
|
|
if (len != 0)
|
|
|
|
break;
|
|
|
|
m_freem(sc->tx_buf_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len < SBNI_MIN_LEN)
|
|
|
|
len = SBNI_MIN_LEN;
|
|
|
|
|
|
|
|
sc->pktlen = len;
|
|
|
|
sc->tx_frameno = (len + sc->maxframe - 1) / sc->maxframe;
|
|
|
|
sc->framelen = min(len, sc->maxframe);
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR0, sbni_inb(sc, CSR0) | TR_REQ);
|
|
|
|
sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
|
|
|
BPF_MTAP(sc->ifp, sc->tx_buf_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
drop_xmit_queue(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
if (sc->tx_buf_p) {
|
|
|
|
m_freem(sc->tx_buf_p);
|
|
|
|
sc->tx_buf_p = NULL;
|
|
|
|
sc->ifp->if_oerrors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
IF_DEQUEUE(&sc->ifp->if_snd, m);
|
|
|
|
if (m == NULL)
|
|
|
|
break;
|
|
|
|
m_freem(m);
|
|
|
|
sc->ifp->if_oerrors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->tx_frameno = 0;
|
|
|
|
sc->framelen = 0;
|
|
|
|
sc->outpos = 0;
|
|
|
|
sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
|
|
|
|
sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
send_frame_header(struct sbni_softc *sc, u_int32_t *crc_p)
|
|
|
|
{
|
|
|
|
u_int32_t crc;
|
|
|
|
u_int len_field;
|
|
|
|
u_char value;
|
|
|
|
|
|
|
|
crc = *crc_p;
|
|
|
|
len_field = sc->framelen + 6; /* CRC + frameno + reserved */
|
|
|
|
|
|
|
|
if (sc->state & FL_NEED_RESEND)
|
|
|
|
len_field |= FRAME_RETRY; /* non-first attempt... */
|
|
|
|
|
|
|
|
if (sc->outpos == 0)
|
|
|
|
len_field |= FRAME_FIRST;
|
|
|
|
|
|
|
|
len_field |= (sc->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD;
|
|
|
|
sbni_outb(sc, DAT, SBNI_SIG);
|
|
|
|
|
|
|
|
value = (u_char)len_field;
|
|
|
|
sbni_outb(sc, DAT, value);
|
|
|
|
crc = CRC32(value, crc);
|
|
|
|
value = (u_char)(len_field >> 8);
|
|
|
|
sbni_outb(sc, DAT, value);
|
|
|
|
crc = CRC32(value, crc);
|
|
|
|
|
|
|
|
sbni_outb(sc, DAT, sc->tx_frameno);
|
|
|
|
crc = CRC32(sc->tx_frameno, crc);
|
|
|
|
sbni_outb(sc, DAT, 0);
|
|
|
|
crc = CRC32(0, crc);
|
|
|
|
*crc_p = crc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if frame tail not needed (incorrect number or received twice),
|
|
|
|
* it won't store, but CRC will be calculated
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
skip_tail(struct sbni_softc *sc, u_int tail_len, u_int32_t crc)
|
|
|
|
{
|
|
|
|
while (tail_len--)
|
|
|
|
crc = CRC32(sbni_inb(sc, DAT), crc);
|
|
|
|
|
|
|
|
return (crc == CRC32_REMAINDER);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
check_fhdr(struct sbni_softc *sc, u_int *framelen, u_int *frameno,
|
|
|
|
u_int *ack, u_int *is_first, u_int32_t *crc_p)
|
|
|
|
{
|
|
|
|
u_int32_t crc;
|
|
|
|
u_char value;
|
|
|
|
|
|
|
|
crc = *crc_p;
|
|
|
|
if (sbni_inb(sc, DAT) != SBNI_SIG)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
value = sbni_inb(sc, DAT);
|
|
|
|
*framelen = (u_int)value;
|
|
|
|
crc = CRC32(value, crc);
|
|
|
|
value = sbni_inb(sc, DAT);
|
|
|
|
*framelen |= ((u_int)value) << 8;
|
|
|
|
crc = CRC32(value, crc);
|
|
|
|
|
|
|
|
*ack = *framelen & FRAME_ACK_MASK;
|
|
|
|
*is_first = (*framelen & FRAME_FIRST) != 0;
|
|
|
|
|
|
|
|
if ((*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
value = sbni_inb(sc, DAT);
|
|
|
|
*frameno = (u_int)value;
|
|
|
|
crc = CRC32(value, crc);
|
|
|
|
|
|
|
|
crc = CRC32(sbni_inb(sc, DAT), crc); /* reserved byte */
|
|
|
|
*framelen -= 2;
|
|
|
|
|
|
|
|
*crc_p = crc;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_rx_buf(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m == NULL) {
|
|
|
|
if_printf(sc->ifp, "cannot allocate header mbuf\n");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We always put the received packet in a single buffer -
|
|
|
|
* either with just an mbuf header or in a cluster attached
|
|
|
|
* to the header. The +2 is to compensate for the alignment
|
|
|
|
* fixup below.
|
|
|
|
*/
|
|
|
|
if (ETHER_MAX_LEN + 2 > MHLEN) {
|
|
|
|
/* Attach an mbuf cluster */
|
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
|
|
if ((m->m_flags & M_EXT) == 0) {
|
|
|
|
m_freem(m);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m->m_pkthdr.len = m->m_len = ETHER_MAX_LEN + 2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The +2 is to longword align the start of the real packet.
|
|
|
|
* (sizeof ether_header == 14)
|
|
|
|
* This is important for NFS.
|
|
|
|
*/
|
|
|
|
m_adj(m, 2);
|
|
|
|
sc->rx_buf_p = m;
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
indicate_pkt(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = sc->ifp;
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
m = sc->rx_buf_p;
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
m->m_pkthdr.len = m->m_len = sc->inppos;
|
|
|
|
sc->rx_buf_p = NULL;
|
|
|
|
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
(*ifp->if_input)(ifp, m);
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routine checks periodically wire activity and regenerates marker if
|
|
|
|
* connect was inactive for a long time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
sbni_timeout(void *xsc)
|
|
|
|
{
|
|
|
|
struct sbni_softc *sc;
|
|
|
|
u_char csr0;
|
|
|
|
|
|
|
|
sc = (struct sbni_softc *)xsc;
|
|
|
|
SBNI_ASSERT_LOCKED(sc);
|
|
|
|
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
if (csr0 & RC_CHK) {
|
|
|
|
|
|
|
|
if (sc->timer_ticks) {
|
|
|
|
if (csr0 & (RC_RDY | BU_EMP))
|
|
|
|
/* receiving not active */
|
|
|
|
sc->timer_ticks--;
|
|
|
|
} else {
|
|
|
|
sc->in_stats.timeout_number++;
|
|
|
|
if (sc->delta_rxl)
|
|
|
|
timeout_change_level(sc);
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
|
|
|
|
csr0 = sbni_inb(sc, CSR0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR0, csr0 | RC_CHK);
|
|
|
|
callout_reset(&sc->wch, hz/SBNI_HZ, sbni_timeout, sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static void
|
|
|
|
card_start(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
sc->timer_ticks = CHANGE_LEVEL_START_TICKS;
|
|
|
|
sc->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);
|
|
|
|
sc->state |= FL_PREV_OK;
|
|
|
|
|
|
|
|
sc->inppos = 0;
|
|
|
|
sc->wait_frameno = 0;
|
|
|
|
|
|
|
|
sbni_outb(sc, CSR1, *(u_char *)&sc->csr1 | PR_RES);
|
|
|
|
sbni_outb(sc, CSR0, EN_INT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static u_char rxl_tab[] = {
|
|
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
|
|
|
|
0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SIZE_OF_TIMEOUT_RXL_TAB 4
|
|
|
|
static u_char timeout_rxl_tab[] = {
|
|
|
|
0x03, 0x05, 0x08, 0x0b
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_initial_values(struct sbni_softc *sc, struct sbni_flags flags)
|
|
|
|
{
|
|
|
|
if (flags.fixed_rxl) {
|
|
|
|
sc->delta_rxl = 0; /* disable receive level autodetection */
|
|
|
|
sc->cur_rxl_index = flags.rxl;
|
|
|
|
} else {
|
|
|
|
sc->delta_rxl = DEF_RXL_DELTA;
|
|
|
|
sc->cur_rxl_index = DEF_RXL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
|
|
|
|
sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
|
|
|
|
sc->maxframe = DEFAULT_FRAME_LEN;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* generate Ethernet address (0x00ff01xxxxxx)
|
|
|
|
*/
|
|
|
|
*(u_int16_t *) sc->enaddr = htons(0x00ff);
|
|
|
|
if (flags.mac_addr) {
|
|
|
|
*(u_int32_t *) (sc->enaddr + 2) =
|
|
|
|
htonl(flags.mac_addr | 0x01000000);
|
|
|
|
} else {
|
|
|
|
*(u_char *) (sc->enaddr + 2) = 0x01;
|
|
|
|
read_random(sc->enaddr + 3, 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SBNI_DUAL_COMPOUND
|
|
|
|
void
|
|
|
|
sbni_add(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
mtx_lock(&headlist_lock);
|
|
|
|
sc->link = sbni_headlist;
|
|
|
|
sbni_headlist = sc;
|
|
|
|
mtx_unlock(&headlist_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sbni_softc *
|
|
|
|
connect_to_master(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
struct sbni_softc *p, *p_prev;
|
|
|
|
|
|
|
|
mtx_lock(&headlist_lock);
|
|
|
|
for (p = sbni_headlist, p_prev = NULL; p; p_prev = p, p = p->link) {
|
|
|
|
if (rman_get_start(p->io_res) == rman_get_start(sc->io_res) + 4 ||
|
|
|
|
rman_get_start(p->io_res) == rman_get_start(sc->io_res) - 4) {
|
|
|
|
p->slave_sc = sc;
|
|
|
|
if (p_prev)
|
|
|
|
p_prev->link = p->link;
|
|
|
|
else
|
|
|
|
sbni_headlist = p->link;
|
|
|
|
mtx_unlock(&headlist_lock);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mtx_unlock(&headlist_lock);
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* SBNI_DUAL_COMPOUND */
|
|
|
|
|
|
|
|
|
|
|
|
/* Receive level auto-selection */
|
|
|
|
|
|
|
|
static void
|
|
|
|
change_level(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
if (sc->delta_rxl == 0) /* do not auto-negotiate RxL */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sc->cur_rxl_index == 0)
|
|
|
|
sc->delta_rxl = 1;
|
|
|
|
else if (sc->cur_rxl_index == 15)
|
|
|
|
sc->delta_rxl = -1;
|
|
|
|
else if (sc->cur_rxl_rcvd < sc->prev_rxl_rcvd)
|
|
|
|
sc->delta_rxl = -sc->delta_rxl;
|
|
|
|
|
|
|
|
sc->csr1.rxl = rxl_tab[sc->cur_rxl_index += sc->delta_rxl];
|
|
|
|
sbni_inb(sc, CSR0); /* it needed for PCI cards */
|
|
|
|
sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
|
|
|
|
|
|
|
|
sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
|
|
|
|
sc->cur_rxl_rcvd = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
timeout_change_level(struct sbni_softc *sc)
|
|
|
|
{
|
|
|
|
sc->cur_rxl_index = timeout_rxl_tab[sc->timeout_rxl];
|
|
|
|
if (++sc->timeout_rxl >= 4)
|
|
|
|
sc->timeout_rxl = 0;
|
|
|
|
|
|
|
|
sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
|
|
|
|
sbni_inb(sc, CSR0);
|
|
|
|
sbni_outb(sc, CSR1, *(u_char *)&sc->csr1);
|
|
|
|
|
|
|
|
sc->prev_rxl_rcvd = sc->cur_rxl_rcvd;
|
|
|
|
sc->cur_rxl_rcvd = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process an ioctl request. This code needs some work - it looks
|
|
|
|
* pretty ugly.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
sbni_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|
|
|
{
|
|
|
|
struct sbni_softc *sc;
|
|
|
|
struct ifreq *ifr;
|
|
|
|
struct thread *td;
|
|
|
|
struct sbni_in_stats *in_stats;
|
|
|
|
struct sbni_flags flags;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
ifr = (struct ifreq *)data;
|
|
|
|
td = curthread;
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
case SIOCSIFFLAGS:
|
|
|
|
/*
|
|
|
|
* If the interface is marked up and stopped, then start it.
|
|
|
|
* If it is marked down and running, then stop it.
|
|
|
|
*/
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
if (ifp->if_flags & IFF_UP) {
|
|
|
|
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
|
|
|
|
sbni_init_locked(sc);
|
|
|
|
} else {
|
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
|
|
sbni_stop(sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
|
|
|
/*
|
|
|
|
* Multicast list has changed; set the hardware filter
|
|
|
|
* accordingly.
|
|
|
|
*/
|
|
|
|
error = 0;
|
|
|
|
/* if (ifr == NULL)
|
|
|
|
error = EAFNOSUPPORT; */
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SBNI specific ioctl
|
|
|
|
*/
|
|
|
|
case SIOCGHWFLAGS: /* get flags */
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
bcopy((caddr_t)IF_LLADDR(sc->ifp)+3, (caddr_t) &flags, 3);
|
|
|
|
flags.rxl = sc->cur_rxl_index;
|
|
|
|
flags.rate = sc->csr1.rate;
|
|
|
|
flags.fixed_rxl = (sc->delta_rxl == 0);
|
|
|
|
flags.fixed_rate = 1;
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
ifr->ifr_data = *(caddr_t*) &flags;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCGINSTATS:
|
|
|
|
in_stats = malloc(sizeof(struct sbni_in_stats), M_DEVBUF,
|
|
|
|
M_WAITOK);
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
bcopy(&sc->in_stats, in_stats, sizeof(struct sbni_in_stats));
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
error = copyout(ifr->ifr_data, in_stats,
|
|
|
|
sizeof(struct sbni_in_stats));
|
|
|
|
free(in_stats, M_DEVBUF);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCSHWFLAGS: /* set flags */
|
|
|
|
/* root only */
|
|
|
|
error = priv_check(td, PRIV_DRIVER);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
flags = *(struct sbni_flags*)&ifr->ifr_data;
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
if (flags.fixed_rxl) {
|
|
|
|
sc->delta_rxl = 0;
|
|
|
|
sc->cur_rxl_index = flags.rxl;
|
|
|
|
} else {
|
|
|
|
sc->delta_rxl = DEF_RXL_DELTA;
|
|
|
|
sc->cur_rxl_index = DEF_RXL;
|
|
|
|
}
|
|
|
|
sc->csr1.rxl = rxl_tab[sc->cur_rxl_index];
|
|
|
|
sc->csr1.rate = flags.fixed_rate ? flags.rate : DEFAULT_RATE;
|
|
|
|
if (flags.mac_addr)
|
|
|
|
bcopy((caddr_t) &flags,
|
|
|
|
(caddr_t) IF_LLADDR(sc->ifp)+3, 3);
|
|
|
|
|
|
|
|
/* Don't be afraid... */
|
|
|
|
sbni_outb(sc, CSR1, *(char*)(&sc->csr1) | PR_RES);
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIOCRINSTATS:
|
|
|
|
SBNI_LOCK(sc);
|
|
|
|
if (!(error = priv_check(td, PRIV_DRIVER))) /* root only */
|
|
|
|
bzero(&sc->in_stats, sizeof(struct sbni_in_stats));
|
|
|
|
SBNI_UNLOCK(sc);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = ether_ioctl(ifp, command, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static u_int32_t
|
|
|
|
calc_crc32(u_int32_t crc, caddr_t p, u_int len)
|
|
|
|
{
|
|
|
|
while (len--)
|
|
|
|
crc = CRC32(*p++, crc);
|
|
|
|
|
|
|
|
return (crc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u_int32_t crc32tab[] __aligned(8) = {
|
|
|
|
0xD202EF8D, 0xA505DF1B, 0x3C0C8EA1, 0x4B0BBE37,
|
|
|
|
0xD56F2B94, 0xA2681B02, 0x3B614AB8, 0x4C667A2E,
|
|
|
|
0xDCD967BF, 0xABDE5729, 0x32D70693, 0x45D03605,
|
|
|
|
0xDBB4A3A6, 0xACB39330, 0x35BAC28A, 0x42BDF21C,
|
|
|
|
0xCFB5FFE9, 0xB8B2CF7F, 0x21BB9EC5, 0x56BCAE53,
|
|
|
|
0xC8D83BF0, 0xBFDF0B66, 0x26D65ADC, 0x51D16A4A,
|
|
|
|
0xC16E77DB, 0xB669474D, 0x2F6016F7, 0x58672661,
|
|
|
|
0xC603B3C2, 0xB1048354, 0x280DD2EE, 0x5F0AE278,
|
|
|
|
0xE96CCF45, 0x9E6BFFD3, 0x0762AE69, 0x70659EFF,
|
|
|
|
0xEE010B5C, 0x99063BCA, 0x000F6A70, 0x77085AE6,
|
|
|
|
0xE7B74777, 0x90B077E1, 0x09B9265B, 0x7EBE16CD,
|
|
|
|
0xE0DA836E, 0x97DDB3F8, 0x0ED4E242, 0x79D3D2D4,
|
|
|
|
0xF4DBDF21, 0x83DCEFB7, 0x1AD5BE0D, 0x6DD28E9B,
|
|
|
|
0xF3B61B38, 0x84B12BAE, 0x1DB87A14, 0x6ABF4A82,
|
|
|
|
0xFA005713, 0x8D076785, 0x140E363F, 0x630906A9,
|
|
|
|
0xFD6D930A, 0x8A6AA39C, 0x1363F226, 0x6464C2B0,
|
|
|
|
0xA4DEAE1D, 0xD3D99E8B, 0x4AD0CF31, 0x3DD7FFA7,
|
|
|
|
0xA3B36A04, 0xD4B45A92, 0x4DBD0B28, 0x3ABA3BBE,
|
|
|
|
0xAA05262F, 0xDD0216B9, 0x440B4703, 0x330C7795,
|
|
|
|
0xAD68E236, 0xDA6FD2A0, 0x4366831A, 0x3461B38C,
|
|
|
|
0xB969BE79, 0xCE6E8EEF, 0x5767DF55, 0x2060EFC3,
|
|
|
|
0xBE047A60, 0xC9034AF6, 0x500A1B4C, 0x270D2BDA,
|
|
|
|
0xB7B2364B, 0xC0B506DD, 0x59BC5767, 0x2EBB67F1,
|
|
|
|
0xB0DFF252, 0xC7D8C2C4, 0x5ED1937E, 0x29D6A3E8,
|
|
|
|
0x9FB08ED5, 0xE8B7BE43, 0x71BEEFF9, 0x06B9DF6F,
|
|
|
|
0x98DD4ACC, 0xEFDA7A5A, 0x76D32BE0, 0x01D41B76,
|
|
|
|
0x916B06E7, 0xE66C3671, 0x7F6567CB, 0x0862575D,
|
|
|
|
0x9606C2FE, 0xE101F268, 0x7808A3D2, 0x0F0F9344,
|
|
|
|
0x82079EB1, 0xF500AE27, 0x6C09FF9D, 0x1B0ECF0B,
|
|
|
|
0x856A5AA8, 0xF26D6A3E, 0x6B643B84, 0x1C630B12,
|
|
|
|
0x8CDC1683, 0xFBDB2615, 0x62D277AF, 0x15D54739,
|
|
|
|
0x8BB1D29A, 0xFCB6E20C, 0x65BFB3B6, 0x12B88320,
|
|
|
|
0x3FBA6CAD, 0x48BD5C3B, 0xD1B40D81, 0xA6B33D17,
|
|
|
|
0x38D7A8B4, 0x4FD09822, 0xD6D9C998, 0xA1DEF90E,
|
|
|
|
0x3161E49F, 0x4666D409, 0xDF6F85B3, 0xA868B525,
|
|
|
|
0x360C2086, 0x410B1010, 0xD80241AA, 0xAF05713C,
|
|
|
|
0x220D7CC9, 0x550A4C5F, 0xCC031DE5, 0xBB042D73,
|
|
|
|
0x2560B8D0, 0x52678846, 0xCB6ED9FC, 0xBC69E96A,
|
|
|
|
0x2CD6F4FB, 0x5BD1C46D, 0xC2D895D7, 0xB5DFA541,
|
|
|
|
0x2BBB30E2, 0x5CBC0074, 0xC5B551CE, 0xB2B26158,
|
|
|
|
0x04D44C65, 0x73D37CF3, 0xEADA2D49, 0x9DDD1DDF,
|
|
|
|
0x03B9887C, 0x74BEB8EA, 0xEDB7E950, 0x9AB0D9C6,
|
|
|
|
0x0A0FC457, 0x7D08F4C1, 0xE401A57B, 0x930695ED,
|
|
|
|
0x0D62004E, 0x7A6530D8, 0xE36C6162, 0x946B51F4,
|
|
|
|
0x19635C01, 0x6E646C97, 0xF76D3D2D, 0x806A0DBB,
|
|
|
|
0x1E0E9818, 0x6909A88E, 0xF000F934, 0x8707C9A2,
|
|
|
|
0x17B8D433, 0x60BFE4A5, 0xF9B6B51F, 0x8EB18589,
|
|
|
|
0x10D5102A, 0x67D220BC, 0xFEDB7106, 0x89DC4190,
|
|
|
|
0x49662D3D, 0x3E611DAB, 0xA7684C11, 0xD06F7C87,
|
|
|
|
0x4E0BE924, 0x390CD9B2, 0xA0058808, 0xD702B89E,
|
|
|
|
0x47BDA50F, 0x30BA9599, 0xA9B3C423, 0xDEB4F4B5,
|
|
|
|
0x40D06116, 0x37D75180, 0xAEDE003A, 0xD9D930AC,
|
|
|
|
0x54D13D59, 0x23D60DCF, 0xBADF5C75, 0xCDD86CE3,
|
|
|
|
0x53BCF940, 0x24BBC9D6, 0xBDB2986C, 0xCAB5A8FA,
|
|
|
|
0x5A0AB56B, 0x2D0D85FD, 0xB404D447, 0xC303E4D1,
|
|
|
|
0x5D677172, 0x2A6041E4, 0xB369105E, 0xC46E20C8,
|
|
|
|
0x72080DF5, 0x050F3D63, 0x9C066CD9, 0xEB015C4F,
|
|
|
|
0x7565C9EC, 0x0262F97A, 0x9B6BA8C0, 0xEC6C9856,
|
|
|
|
0x7CD385C7, 0x0BD4B551, 0x92DDE4EB, 0xE5DAD47D,
|
|
|
|
0x7BBE41DE, 0x0CB97148, 0x95B020F2, 0xE2B71064,
|
|
|
|
0x6FBF1D91, 0x18B82D07, 0x81B17CBD, 0xF6B64C2B,
|
|
|
|
0x68D2D988, 0x1FD5E91E, 0x86DCB8A4, 0xF1DB8832,
|
|
|
|
0x616495A3, 0x1663A535, 0x8F6AF48F, 0xF86DC419,
|
|
|
|
0x660951BA, 0x110E612C, 0x88073096, 0xFF000000
|
|
|
|
};
|