Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 3 Jan 2001 15:08:32 -0200
From:      Sergio de Souza Prallon <prallon@tmp.com.br>
To:        Andrey Lakhno <land@dnepr.net>
Cc:        freebsd-isdn@FreeBSD.OR
Subject:   Re: 2 Teles PCI cards in one PC
Message-ID:  <20010103150832.A19526@tmp.com.br>
Resent-Message-ID: <200101031709.PAA20068@liliput.tmp.com.br>
In-Reply-To: <20010103002019.A11223@dnepr.net>; from land@dnepr.net on Wed, Jan 03, 2001 at 12:20:19AM %2B0200
References:  <20010102093844.A10729@dnepr.net> <20010102085741.ED8243A1@hcswork.hcs.de> <20010102110719.A829@dnepr.net> <20010102151432.A21821@tmp.com.br> <20010103002019.A11223@dnepr.net>

next in thread | previous in thread | raw e-mail | index | archive | help

--LZvS9be/3tNcYl/X
Content-Type: text/plain; charset=us-ascii

On Wed, Jan 03, 2001 at 12:20:19AM +0200, Andrey Lakhno wrote:
> Hi Sergio!
> 
Hi Andrey,

> 
> Does kernel sppp supports multilink ppp ?
> 

No. It's simpler than the userland one. I believe, however, it's lighter
than the later.

> 
> To this letter attached ppp.conf, isdnd.rc and script of kernel debug session.
> 

Many thanks for the GDB output. Now I know I really need a second 
machine... 8-)

I didn't update the web site yet, because I didn't finish a last minute
patch to the kernel SPPP driver. Anyway, please try to substitute the
file i4b_itjc_pci.c you have with the one attached on this mail. It's a
`release candidate' for the beta version of the driver. I successfully
established a ML-PPP connection to my ISP using a channel in each of my
two cards. Let me know if it works.

Regards,

--
Prallon

--LZvS9be/3tNcYl/X
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="i4b_itjc_pci.c"

/*
 *   Copyright (c) 2000, 2001 Sergio Prallon. All rights reserved.
 *
 *   Redistribution 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, 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.
 *   3. Neither the name of the author nor the names of any co-contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *   4. Altered versions must be plainly marked as such, and must not be
 *      misrepresented as being the original software and/or documentation.
 *   
 *   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 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *   SUCH DAMAGE.
 *
 *---------------------------------------------------------------------------
 *
 *	i4b_itjc_pci.c: NetJet-S hardware driver
 *	----------------------------------------
 *
 *	$Id: i4b_itjc_pci.c,v 1.2 2001/01/03 14:35:53 root Exp root $
 *
 * $FreeBSD$
 *
 *      last edit-date: []
 *
 *---------------------------------------------------------------------------*/

#include "itjc.h"
#include "opt_i4b.h"
#include "pci.h"

#if (NITJC > 0)

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>

#include <machine/clock.h>
#include <machine/bus_pio.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/bus.h>
#include <sys/rman.h>

#include <pci/pcireg.h>
#include <pci/pcivar.h>

#include <sys/socket.h>
#include <net/if.h>

#include <machine/i4b_debug.h>
#include <machine/i4b_ioctl.h>
#include <machine/i4b_trace.h>

#include <i4b/include/i4b_global.h>
#include <i4b/include/i4b_mbuf.h>

#include <i4b/layer1/i4b_l1.h>
#include <i4b/layer1/i4b_hdlc.h>
#include <i4b/layer1/isic/i4b_isic.h>
#include <i4b/layer1/isic/i4b_isac.h>

#include <i4b/layer1/itjc/i4b_itjc_ext.h>

#define PCI_TJNET_VID (0xe159)
#define PCI_TJ300_DID (0x0001)


#if defined(ITJC_VJ_WORKAROUND)
/*
 * We need to reserve this amount of memory before the start of data
 * when allocating an mbuf for receiving. This is taken as granted
 * by sl_uncompress.
 */
#   define ITJC_VJ_HDR		(128)
#   define ITJC_MAX_RAW_LEN	(BCH_MAX_DATALEN)
#   define ITJC_MAX_HDLC_LEN	(BCH_MAX_DATALEN - ITJC_VJ_HDR)
#else
#   define ITJC_MAX_RAW_LEN	(BCH_MAX_DATALEN)
#   define ITJC_MAX_HDLC_LEN	(BCH_MAX_DATALEN)
#endif


/*
 * Function prototypes
 */

static int  itjc_probe(device_t dev);
static int  itjc_attach(device_t dev);
static void itjc_shutdown(device_t dev);
static void itjc_intr(void *xsc);
static int  itjc_dma_start(struct l1_softc *sc);
static void itjc_dma_stop(struct l1_softc *sc);
static void itjc_isac_intr(struct l1_softc *sc);
static void itjc_init_linktab(struct l1_softc *sc);
static void itjc_bchannel_setup(int unit, int h_chan, int bprot, 
	int activate);
static void itjc_bchannel_stat(int unit, int h_chan, bchan_statistics_t *bsp);


/*
 * Shorter names to bus resource manager routines.
 */

#define itjc_bus_setup(sc)						\
	bus_space_handle_t h =						\
		rman_get_bushandle((sc)->sc_resources.io_base[0]);	\
	bus_space_tag_t    t =						\
		rman_get_bustag((sc)->sc_resources.io_base[0]);

#define itjc_read_1(port)	(bus_space_read_1(t, h, (port)))
#define itjc_read_4(port)	(bus_space_read_4(t, h, (port)))
#define itjc_write_1(port, data) (bus_space_write_1(t, h, (port), (data)))
#define itjc_write_4(port, data) (bus_space_write_4(t, h, (port), (data)))
#define itjc_read_multi_1(port, buf, size)				\
	(bus_space_read_multi_1(t, h, (port), (buf), (size)))
#define itjc_write_multi_1(port, buf, size)				\
	(bus_space_write_multi_1(t, h, (port), (buf), (size)))


/*---------------------------------------------------------------------------*
 *	Glue data to register ourselves as a PCI device driver.
 *---------------------------------------------------------------------------*/

static device_method_t itjc_pci_methods[] =
{
	/* Device interface */
	DEVMETHOD(device_probe,		itjc_probe),
	DEVMETHOD(device_attach,	itjc_attach),
	DEVMETHOD(device_shutdown,	itjc_shutdown),

	/* bus interface */
	DEVMETHOD(bus_print_child,	bus_generic_print_child),
	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),

	{ 0, 0 }
};

static driver_t itjc_pci_driver =
{
	"itjc",
	itjc_pci_methods,
	sizeof(struct l1_softc)
};

static devclass_t itjc_pci_devclass;

DRIVER_MODULE(netjet, pci, itjc_pci_driver, itjc_pci_devclass, 0, 0);

/*
 * Jump table for multiplex routines.
 */

struct i4b_l1mux_func itjc_l1mux_func =
{
	itjc_ret_linktab,
	itjc_set_linktab,
	itjc_mph_command_req,
	itjc_ph_data_req,
	itjc_ph_activate_req,
};

struct l1_softc *itjc_scp[ITJC_MAXUNIT];


/*---------------------------------------------------------------------------*
 *	Tiger300/320 PCI ASIC registers.
 *---------------------------------------------------------------------------*/

/*
 *	Register offsets from i/o base.
 */
enum tiger_regs
{
	TIGER_RESET_PIB_CL_TIME	= 0x00,
	TIGER_DMA_OPER		= 0x01,
	TIGER_AUX_PORT_CNTL	= 0x02,
	TIGER_AUX_PORT_DATA	= 0x03,
	TIGER_INT0_MASK		= 0x04,
	TIGER_INT1_MASK		= 0x05,
	TIGER_INT0_STATUS	= 0x06,
	TIGER_INT1_STATUS	= 0x07,
	TIGER_DMA_WR_START_ADDR	= 0x08,
	TIGER_DMA_WR_INT_ADDR	= 0x0C,
	TIGER_DMA_WR_END_ADDR	= 0x10,
	TIGER_DMA_WR_CURR_ADDR	= 0x14,
	TIGER_DMA_RD_START_ADDR	= 0x18,
	TIGER_DMA_RD_INT_ADDR	= 0x1C,
	TIGER_DMA_RD_END_ADDR	= 0x20,
	TIGER_DMA_RD_CURR_ADDR	= 0x24,
	TIGER_PULSE_COUNTER	= 0x28,
};

/*
 * Bits on the above registers.
 */

enum tiger_reg_bits
{
/* Reset and PIB Cycle Timing */

	TIGER_DMA_OP_MODE_MASK		= 0x80,
		TIGER_SELF_ADDR_DMA	= 0x00,	/* Wrap around ending addr */
		TIGER_NORMAL_DMA	= 0x80,	/* Stop at ending addr */

	TIGER_DMA_INT_MODE_MASK		= 0x40,
		TIGER_DONT_LATCH_DMA_INT= 0x00,	/* Bits on int0 status will be
						   set only while curr addr
						   equals int or end addr */
		TIGER_LATCH_DMA_INT	= 0x40,	/* Bits on int0 status remain
						   set until cleared by CPU */

	TIGER_PIB_CYCLE_TIMING_MASK	= 0x30,
		TIGER_PIB_3_CYCLES	= 0x00,
		TIGER_PIB_5_CYCLES	= 0x01,
		TIGER_PIB_12_CYCLES	= 0x10,

	TIGER_RESET_MASK		= 0x0F,
		TIGER_RESET_PULSE_COUNT	= 0x08,
		TIGER_RESET_SERIAL_PORT	= 0x04,
		TIGER_RESET_DMA_LOGIC	= 0x02,
		TIGER_RESET_EXTERNAL	= 0x01,
		TIGER_RESET_ALL		= 0x0F,
	
/* DMA Operation */
	TIGER_DMA_RESTART_MASK		= 0x02,
		TIGER_HOLD_DMA		= 0x00,
		TIGER_RESTART_DMA	= 0x00,

	TIGER_DMA_ENABLE_MASK		= 0x01,
		TIGER_ENABLE_DMA	= 0x01,
		TIGER_DISABLE_DMA	= 0x00,

/* AUX Port Control & Data plus Interrupt 1 Mask & Status  */
	TIGER_AUX_7_MASK		= 0x80,
	TIGER_AUX_6_MASK		= 0x40,
	TIGER_AUX_5_MASK		= 0x20,
	TIGER_AUX_4_MASK		= 0x10,
	TIGER_ISAC_INT_MASK		= 0x10,
	TIGER_AUX_3_MASK		= 0x08,
	TIGER_AUX_2_MASK		= 0x04,
	TIGER_AUX_1_MASK		= 0x02,
	TIGER_AUX_0_MASK		= 0x01,

/* AUX Port Control */
		TIGER_AUX_7_IS_INPUT	= 0x00,
		TIGER_AUX_7_IS_OUTPUT	= 0x80,
		TIGER_AUX_6_IS_INPUT	= 0x00,
		TIGER_AUX_6_IS_OUTPUT	= 0x40,
		TIGER_AUX_5_IS_INPUT	= 0x00,
		TIGER_AUX_5_IS_OUTPUT	= 0x20,
		TIGER_AUX_4_IS_INPUT	= 0x00,
		TIGER_AUX_4_IS_OUTPUT	= 0x10,
		TIGER_AUX_3_IS_INPUT	= 0x00,
		TIGER_AUX_3_IS_OUTPUT	= 0x80,
		TIGER_AUX_2_IS_INPUT	= 0x00,
		TIGER_AUX_2_IS_OUTPUT	= 0x40,
		TIGER_AUX_1_IS_INPUT	= 0x00,
		TIGER_AUX_1_IS_OUTPUT	= 0x20,
		TIGER_AUX_0_IS_INPUT	= 0x00,
		TIGER_AUX_0_IS_OUTPUT	= 0x10,
		TIGER_AUX_NJ_DEFAULT	= 0xEF, /* All but ISAC int is output */

/* Interrupt 0 Mask & Status */
	TIGER_PCI_TARGET_ABORT_INT_MASK	= 0x20,
		TIGER_NO_TGT_ABORT_INT	= 0x00,
		TIGER_TARGET_ABORT_INT	= 0x20,
	TIGER_PCI_MASTER_ABORT_INT_MASK	= 0x10,
		TIGER_NO_MST_ABORT_INT	= 0x00,
		TIGER_MASTER_ABORT_INT	= 0x10,
	TIGER_DMA_RD_END_INT_MASK	= 0x08,
		TIGER_NO_RD_END_INT	= 0x00,
		TIGER_RD_END_INT	= 0x08,
	TIGER_DMA_RD_INT_INT_MASK	= 0x04,
		TIGER_NO_RD_INT_INT	= 0x00,
		TIGER_RD_INT_INT	= 0x04,
	TIGER_DMA_WR_END_INT_MASK	= 0x02,
		TIGER_NO_WR_END_INT	= 0x00,
		TIGER_WR_END_INT	= 0x02,
	TIGER_DMA_WR_INT_INT_MASK	= 0x01,
		TIGER_NO_WR_INT_INT	= 0x00,
		TIGER_WR_INT_INT	= 0x01,

/* Interrupt 1 Mask & Status */
		TIGER_NO_AUX_7_INT	= 0x00,
		TIGER_AUX_7_INT		= 0x80,
		TIGER_NO_AUX_6_INT	= 0x00,
		TIGER_AUX_6_INT		= 0x40,
		TIGER_NO_AUX_5_INT	= 0x00,
		TIGER_AUX_5_INT		= 0x20,
		TIGER_NO_AUX_4_INT	= 0x00,
		TIGER_AUX_4_INT		= 0x10,
		TIGER_NO_ISAC_INT	= 0x00,
		TIGER_ISAC_INT		= 0x10,
		TIGER_NO_AUX_3_INT	= 0x00,
		TIGER_AUX_3_INT		= 0x08,
		TIGER_NO_AUX_2_INT	= 0x00,
		TIGER_AUX_2_INT		= 0x04,
		TIGER_NO_AUX_1_INT	= 0x00,
		TIGER_AUX_1_INT		= 0x02,
		TIGER_NO_AUX_0_INT	= 0x00,
		TIGER_AUX_0_INT		= 0x01
};

/*
 * Peripheral Interface Bus definitions. This is an ISA like bus
 * created by the Tiger ASIC to keep ISA chips like the ISAC happy
 * on a PCI environment.
 *
 * Since the PIB only supplies 4 addressing lines, the 2 higher bits
 * (A4 & A5) of the ISAC register addresses are wired on the 2 lower
 * AUX lines. Another restriction is that all I/O to the PIB (8bit
 * wide) is mapped on the PCI side as 32bit data. So the PCI address
 * of a given ISAC register has to be multiplied by 4 before being
 * added to the PIB base offset.
 */
enum tiger_pib_regs_defs
{
	/* Offset from the I/O base to the ISAC registers. */
	PIB_OFFSET		= 0xC0,
	PIB_LO_ADDR_MASK	= 0x0F,		
	PIB_HI_ADDR_MASK	= 0x30,
	PIB_LO_ADDR_SHIFT	= 2,	/* Align on dword boundary */
	PIB_HI_ADDR_SHIFT	= 4	/* Right shift to AUX_1 & AUX_0 */
};


#define	itjc_set_pib_addr_msb(a)					\
(									\
	itjc_write_1(TIGER_AUX_PORT_DATA,				\
		((a) & PIB_HI_ADDR_MASK) >> PIB_HI_ADDR_SHIFT)		\
)

#define	itjc_pib_2_pci(a)						\
(									\
	(((a) & PIB_LO_ADDR_MASK) << PIB_LO_ADDR_SHIFT) + PIB_OFFSET	\
)

#define itjc_get_dma_offset(ctx,reg)					\
(									\
	(u_int16_t)((bus_addr_t)itjc_read_4((reg)) - (ctx)->bus_addr)	\
)


/*
 * IOM-2 serial channel 0 DMA data ring buffers.
 *
 * The Tiger300/320 ASIC do not nothing more than transfer via DMA the
 * first 32 bits of every IOM-2 frame on the serial interface to the
 * ISAC. So we have no framing/deframing facilities like we would have
 * with an HSCX, having to do the job with CPU cycles. On the plus side
 * we are able to specify large rings which can limit the occurrence of
 * over/underruns.
 */

enum
{
	ITJC_RING_SLOT_WORDS	= 64,
	ITJC_RING_WORDS		= 3 * ITJC_RING_SLOT_WORDS,
	ITJC_RING_SLOT_BYTES	= 4 * ITJC_RING_SLOT_WORDS,
	ITJC_RING_BYTES		= 4 * ITJC_RING_WORDS,
	ITJC_DMA_POOL_WORDS	= 2 * ITJC_RING_WORDS,
	ITJC_DMA_POOL_BYTES	= 4 * ITJC_DMA_POOL_WORDS
};

#define	itjc_ring_add(x, d)	(((x) + 4 * (d)) % ITJC_RING_BYTES)
#define itjc_ring_sub(x, d)	(((x) + ITJC_RING_BYTES - 4 * (d))	\
					% ITJC_RING_BYTES)


enum
{
	TIGER_CH_A		= 0,
	TIGER_CH_B		= 1,

	HSCX_CH_A		= 0,	/* For compatibility reasons. */
	HSCX_CH_B		= 1,
};

enum
{
	ITJC_TEL_SILENCE_BYTE	= 0x00,
	ITJC_HDLC_FLAG_BYTE	= 0x7E,
	ITJC_HDLC_ABORT_BYTE	= 0xFF
};

/*
 * Hardware DMA control block (one per card).
 */
typedef enum
{
	ITJC_DS_LOAD_FAILED	= -1,
	ITJC_DS_FREE		=  0,
	ITJC_DS_LOADING,
	ITJC_DS_STOPPED,
	ITJC_DS_RUNNING
}
	dma_state_t;

typedef struct
{
	dma_state_t	state;
	u_int8_t	*pool;
	bus_addr_t	bus_addr;
	bus_dma_tag_t	tag;
	bus_dmamap_t	map;
	int		error;
}
	dma_context_t;

dma_context_t
	dma_context	[ ITJC_MAXUNIT ];

/*
 * B-channel DMA control blocks (4 per card -- 1 RX & 1 TX per channel).
 */
typedef enum
{
	ITJC_RS_IDLE	= 0,
	ITJC_RS_ACTIVE
}
	dma_rx_state_t;

typedef enum
{
	ITJC_TS_IDLE	= 0,
	ITJC_TS_ACTIVE,
	ITJC_TS_AFTER_XDU
}
	dma_tx_state_t;

typedef struct
{
	u_int8_t	*ring;
	bus_addr_t	bus_addr;
	u_int16_t	next_read;
	u_int16_t	hdlc_len;
	u_int16_t	hdlc_tmp;
	u_int16_t	hdlc_crc;
	u_int16_t	hdlc_ib;
	u_int8_t	hdlc_blevel;
	u_int8_t	hdlc_flag;
	dma_rx_state_t	state;
}
	dma_rx_context_t;

typedef struct
{
	u_int8_t	*ring;
	bus_addr_t	bus_addr;
	u_int16_t	next_write;
	u_int32_t	hdlc_tmp;
	u_int16_t	hdlc_blevel;
	u_int16_t	hdlc_crc;
	u_int16_t	hdlc_ib;
	u_int16_t	next_frame;
	u_int16_t	filled;
	u_int8_t	hdlc_flag;
	dma_tx_state_t	state;
}
	dma_tx_context_t;

dma_rx_context_t
	dma_rx_context	[ ITJC_MAXUNIT ] [ 2 ];

dma_tx_context_t
	dma_tx_context	[ ITJC_MAXUNIT ] [ 2 ];

/*
 * Used by the mbuf handling functions.
 */
typedef enum
{
	ITJC_MB_CURR = 0,
	ITJC_MB_NEXT = 1,
	ITJC_MB_NEW  = 2
}
	which_mb_t;


/*---------------------------------------------------------------------------*
 *	itjc_map_callback - get DMA bus address from resource mgr.
 *---------------------------------------------------------------------------*/
static void
itjc_map_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
	dma_context_t		*ctx = (dma_context_t *)arg;

	if (error)
	{
		ctx->error = error;
		ctx->state = ITJC_DS_LOAD_FAILED;
		return;
	}

        ctx->bus_addr = segs->ds_addr;
	ctx->state = ITJC_DS_STOPPED;
}


/*---------------------------------------------------------------------------*
 *	itjc_dma_start - Complete DMA setup & start the Tiger DMA engine.
 *---------------------------------------------------------------------------*/
static int
itjc_dma_start(struct l1_softc *sc)
{
	int			unit	= sc->sc_unit;
	dma_context_t		*ctx	= &dma_context[unit];
	dma_rx_context_t	*rxc	= &dma_rx_context[unit][0];
	dma_tx_context_t	*txc	= &dma_tx_context[unit][0];
	bus_addr_t		ba;
	u_int8_t		i;
	u_int32_t		*pool_end,
				*ip;

	itjc_bus_setup(sc);

	/* See if it is already running. */

	if (ctx->state == ITJC_DS_RUNNING)
		return 0;

	if (ctx->state == ITJC_DS_LOAD_FAILED)
	{
		NDBGL1(L1_ERROR, "itjc%d: dma_start: DMA map loading "
			"failed (error=%d).\n", unit, ctx->error);
		return 1;
	}

	if (ctx->state != ITJC_DS_STOPPED)
	{
		NDBGL1(L1_ERROR, "itjc%d: dma_start: Unexpected DMA "
			"state (%d).\n", unit, ctx->state);
		return 1;
	}

	/*
	 * Initialize the DMA control structures (hardware & B-channel).
	 */
	ba = ctx->bus_addr;

	txc->ring = ctx->pool + TIGER_CH_A;
	rxc->ring = ctx->pool + TIGER_CH_A + ITJC_RING_BYTES;

	txc->bus_addr = ba;
	rxc->bus_addr = ba + ITJC_RING_BYTES;

	++rxc; ++txc;

	txc->ring = ctx->pool + TIGER_CH_B;
	rxc->ring = ctx->pool + TIGER_CH_B + ITJC_RING_BYTES;

	txc->bus_addr = ba;
	rxc->bus_addr = ba + ITJC_RING_BYTES;

	/*
	 * Fill the DMA ring buffers with IOM-2 channel 0 frames made of
	 * idle/abort sequences for the B & D channels and NOP for IOM-2
	 * cmd/ind, monitor handshake & data.
	 */
	pool_end = (u_int32_t *)ctx->pool + ITJC_DMA_POOL_WORDS;
	for (ip = (u_int32_t *)ctx->pool; ip < pool_end; ++ip)
		*ip = 0xFFFFFFFF;

	/*
	 * Program the Tiger DMA gears.
	 */

	itjc_write_4(TIGER_DMA_WR_START_ADDR, ba);
	itjc_write_4(TIGER_DMA_WR_INT_ADDR, ba + ITJC_RING_SLOT_BYTES - 4);
	itjc_write_4(TIGER_DMA_WR_END_ADDR, ba + ITJC_RING_BYTES - 4);

	ba += ITJC_RING_BYTES;

	itjc_write_4(TIGER_DMA_RD_START_ADDR, ba);
	itjc_write_4(TIGER_DMA_RD_INT_ADDR, ba + ITJC_RING_SLOT_BYTES * 2 - 4);
	itjc_write_4(TIGER_DMA_RD_END_ADDR, ba + ITJC_RING_BYTES - 4);

	itjc_write_1(TIGER_INT0_MASK, 
		TIGER_WR_END_INT | TIGER_WR_INT_INT | TIGER_RD_INT_INT);

	itjc_write_1(TIGER_DMA_OPER, TIGER_ENABLE_DMA);

	/*
	 * See if it really started.
	 */
	ba = itjc_read_4(TIGER_DMA_RD_CURR_ADDR);
	for (i = 0; i < 10; ++i)
	{
		DELAY(SEC_DELAY/1000);
		if (ba != itjc_read_4(TIGER_DMA_RD_CURR_ADDR))
		{
			ctx->state = ITJC_DS_RUNNING;
			return 0;
		}
	}

	NDBGL1(L1_ERROR, "itjc%d: dma_start: DMA start failed.\n ", unit);
	return 1;
}


/*---------------------------------------------------------------------------*
 *	itjc_dma_stop - Stop the Tiger DMA engine.
 *---------------------------------------------------------------------------*/
void
itjc_dma_stop(struct l1_softc *sc)
{
	dma_context_t		*ctx	= &dma_context[sc->sc_unit];

	itjc_bus_setup(sc);

	/* Only stop the DMA if it is running. */

	if (ctx->state != ITJC_DS_RUNNING)
		return;

	itjc_write_1(TIGER_DMA_OPER, TIGER_DISABLE_DMA);
	DELAY(SEC_DELAY/1000);

	ctx->state = ITJC_DS_STOPPED;
}


/*---------------------------------------------------------------------------*
 *	itjc_bchannel_dma_setup - The DMA side of itjc_bchannel_setup.
 *---------------------------------------------------------------------------*/
static void
itjc_bchannel_dma_setup(struct l1_softc *sc, int h_chan, int activate)
{
	dma_rx_context_t	*rxc  = &dma_rx_context[sc->sc_unit][h_chan];
	dma_tx_context_t	*txc  = &dma_tx_context[sc->sc_unit][h_chan];

	l1_bchan_state_t	*chan = &sc->sc_chan[h_chan];

	u_int8_t		fill_byte,
				*ring_end,
				*cp;

	int			s = SPLI4B();

	itjc_bus_setup(sc);

	if (activate)
	{
		/*
		 * Get the DMA engine going if it's not running already.
		 */
		itjc_dma_start(sc);

		rxc->hdlc_len	= rxc->hdlc_tmp    = rxc->hdlc_crc  = 0;
		rxc->hdlc_ib	= rxc->hdlc_blevel = rxc->hdlc_flag = 0;

		txc->hdlc_tmp	= txc->hdlc_blevel = txc->hdlc_crc  = 0;
		txc->hdlc_ib	= 0;
		txc->hdlc_flag	= 2;
		txc->filled	= 0;

		if (chan->bprot == BPROT_NONE)
			fill_byte = ITJC_TEL_SILENCE_BYTE;
		else
			fill_byte = ITJC_HDLC_ABORT_BYTE;

		ring_end = rxc->ring + ITJC_RING_BYTES;
		for (cp = rxc->ring; cp < ring_end; cp += 4)
			*cp = fill_byte;

		ring_end = txc->ring + ITJC_RING_BYTES;
		for (cp = txc->ring; cp < ring_end; cp += 4)
			*cp = fill_byte;

		rxc->next_read  =
			itjc_get_dma_offset(rxc, TIGER_DMA_RD_CURR_ADDR);

		txc->next_frame = txc->next_write =
			itjc_get_dma_offset(txc, TIGER_DMA_WR_CURR_ADDR);

		rxc->state	= ITJC_RS_ACTIVE;
		txc->state	= ITJC_TS_AFTER_XDU;
	}
	else
	{
		dma_rx_context_t	*rxc2;

		txc->state	= ITJC_TS_IDLE;
		rxc->state	= ITJC_RS_IDLE;

		rxc2 = &dma_rx_context[sc->sc_unit][0];

		if (rxc2->state == ITJC_RS_IDLE 
		&& rxc2[1].state == ITJC_RS_IDLE)
			itjc_dma_stop(sc);
	}

	splx(s);
}


/*---------------------------------------------------------------------------*
 *	Mbuf & if_queues management routines.
 *---------------------------------------------------------------------------*/

static u_int8_t *
itjc_get_rx_mbuf(l1_bchan_state_t *chan, u_int8_t **dst_end_p,
which_mb_t which)
{
	struct mbuf	*mbuf = chan->in_mbuf;

	if (mbuf == NULL && which == ITJC_MB_NEW)
	{
		if ((mbuf = i4b_Bgetmbuf(ITJC_MAX_RAW_LEN)) == NULL)
			panic("itjc_get_rx_mbuf: cannot allocate mbuf!");

#if defined(ITJC_VJ_WORKAROUND)
		if (chan->bprot != BPROT_NONE)
			(u_int8_t *)(mbuf->m_data) += ITJC_VJ_HDR;
#endif

		chan->in_mbuf  = mbuf;
		chan->in_cbptr = (u_int8_t *)mbuf->m_data;
		chan->in_len   = 0;
	}

	if (dst_end_p != NULL)
	{
		if (mbuf != NULL)
			*dst_end_p = (u_int8_t *)(mbuf->m_data)
				+ ((chan->bprot == BPROT_NONE) ?
					ITJC_MAX_RAW_LEN
				:
					ITJC_MAX_HDLC_LEN);
		else
			*dst_end_p = NULL;
	}

	return chan->in_cbptr;
}


static void
itjc_save_rx_mbuf(l1_bchan_state_t *chan, u_int8_t * dst)
{
	struct mbuf	*mbuf = chan->in_mbuf;

	if (dst != NULL && mbuf != NULL)
	{
		chan->in_cbptr = dst;
		chan->in_len   = dst - (u_int8_t *)mbuf->m_data;
	}
	else if (dst == NULL && mbuf == NULL)
	{
		chan->in_cbptr = NULL;
		chan->in_len   = 0;
	}
	else
		panic("itjc_save_rx_mbuf: stale pointer dst=%p mbuf=%p "
			"in_cbptr=%p in_len=%d", dst, mbuf, 
			chan->in_cbptr, chan->in_len);
}


static void
itjc_free_rx_mbuf(l1_bchan_state_t *chan)
{
	struct mbuf	*mbuf = chan->in_mbuf;

	if (mbuf != NULL)
		i4b_Bfreembuf(mbuf);

	chan->in_mbuf  = NULL;
	chan->in_cbptr = NULL;
	chan->in_len   = 0;
}


static void
itjc_put_rx_mbuf(struct l1_softc *sc, l1_bchan_state_t *chan, u_int16_t len)
{
	i4b_trace_hdr_t	hdr;
	struct mbuf	*mbuf	 = chan->in_mbuf;
	u_int8_t	*data	 = mbuf->m_data;
	int		activity = 1;

	mbuf->m_pkthdr.len = mbuf->m_len = len;

	if (sc->sc_trace & TRACE_B_RX)
	{
		hdr.unit = L0ITJCUNIT(sc->sc_unit);
		hdr.type = (chan->channel == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
		hdr.dir = FROM_NT;
		hdr.count = ++sc->sc_trace_bcount;
		MICROTIME(hdr.time);
		i4b_l1_trace_ind(&hdr, len, data);
	}

	if (chan->bprot == BPROT_NONE)
	{
		activity = ! i4b_l1_bchan_tel_silence(data, len);
				
		/* move rx'd data to rx queue */

		if (! IF_QFULL(&chan->rx_queue))
		{
			IF_ENQUEUE(&chan->rx_queue, mbuf);
		}
		else
		{
			i4b_Bfreembuf(mbuf);
			len = 0;
		}
	}

	if (len != 0)
	{
		chan->rxcount += len;

		(*chan->isic_drvr_linktab->bch_rx_data_ready)
			(chan->isic_drvr_linktab->unit);
	}
				
	if (activity)
		(*chan->isic_drvr_linktab->bch_activity)
			(chan->isic_drvr_linktab->unit, ACT_RX);

	chan->in_mbuf  = NULL;
	chan->in_cbptr = NULL;
	chan->in_len   = 0;
}


#define itjc_free_tx_mbufs(chan)					\
{									\
	i4b_Bfreembuf((chan)->out_mbuf_head);				\
	(chan)->out_mbuf_cur = (chan)->out_mbuf_head = NULL;		\
	(chan)->out_mbuf_cur_ptr = NULL;				\
	(chan)->out_mbuf_cur_len = 0;					\
}


static u_int16_t
itjc_get_tx_mbuf(struct l1_softc *sc, l1_bchan_state_t *chan,
	u_int8_t **src_p, which_mb_t which)
{
	i4b_trace_hdr_t	hdr;
	struct mbuf	*mbuf = chan->out_mbuf_cur;
	u_int8_t	activity = 1;
	u_int16_t	len;
	void		*data;

	switch (which)
	{
	case ITJC_MB_CURR:
		if (mbuf != NULL)
		{
			*src_p = chan->out_mbuf_cur_ptr;
			return   chan->out_mbuf_cur_len;
		}

		break;

	case ITJC_MB_NEXT:
		if (mbuf != NULL)
		{
			chan->txcount += mbuf->m_len;

			mbuf = mbuf->m_next;

			if (mbuf != NULL)
				goto new_mbuf;
		}

		chan->out_mbuf_cur_ptr = *src_p = NULL;
		chan->out_mbuf_cur_len = 0;

		if (chan->out_mbuf_head != NULL)
		{
			i4b_Bfreembuf(chan->out_mbuf_head);
			chan->out_mbuf_head = NULL;
		}

		return 0;

	case ITJC_MB_NEW:
		if (mbuf != NULL)
			chan->txcount += mbuf->m_len;
	}

	if (chan->out_mbuf_head != NULL)
		i4b_Bfreembuf(chan->out_mbuf_head);

	IF_DEQUEUE(&chan->tx_queue, mbuf);

	if (mbuf == NULL)
	{
		chan->out_mbuf_cur = chan->out_mbuf_head = NULL;
		chan->out_mbuf_cur_ptr = *src_p = NULL;
		chan->out_mbuf_cur_len = 0;

		chan->state &= ~(HSCX_TX_ACTIVE);

		(*chan->isic_drvr_linktab->bch_tx_queue_empty)
			(chan->isic_drvr_linktab->unit);

		return 0;
	}

	chan->out_mbuf_head = mbuf;

new_mbuf:
	chan->out_mbuf_cur	= mbuf;
	chan->out_mbuf_cur_ptr	= data = mbuf->m_data;
	chan->out_mbuf_cur_len	= len  = mbuf->m_len;

	chan->state |= HSCX_TX_ACTIVE;

	if (sc->sc_trace & TRACE_B_TX)
	{
		hdr.unit = L0ITJCUNIT(sc->sc_unit);
		hdr.type = (chan->channel == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
		hdr.dir = FROM_TE;
		hdr.count = ++sc->sc_trace_bcount;
		MICROTIME(hdr.time);
		i4b_l1_trace_ind(&hdr, len, data);
	}

	if (chan->bprot == BPROT_NONE)
		activity = ! i4b_l1_bchan_tel_silence(data, len);

	if (activity)
		(*chan->isic_drvr_linktab->bch_activity)
			(chan->isic_drvr_linktab->unit, ACT_TX);

	*src_p = data;
	return len;
}


#define itjc_save_tx_mbuf(chan, src, dst)				\
(									\
	(chan)->out_mbuf_cur != NULL ?					\
	(								\
		(chan)->out_mbuf_cur_ptr = (src),			\
		(chan)->out_mbuf_cur_len = (len)			\
	)								\
	:								\
		0							\
)


/*---------------------------------------------------------------------------*
 *	B-channel interrupt service routines.
 *---------------------------------------------------------------------------*/

/*
 * Since the Tiger ASIC doesn't produce a XMIT underflow indication,
 * we need to deduce it ourselves. This is somewhat tricky because we
 * are dealing with modulo m arithmetic. The idea here is to have a
 * "XDU zone" ahead of the writing pointer sized 1/3 of total ring
 * length (a ring slot). If the hardware DMA pointer is found there we
 * consider that a XDU has occurred. To complete the scheme, we never
 * let the ring have more than 2 slots of (unsent) data and adjust the
 * interrupt registers to cause an interrupt at every slot.
 */
static u_int8_t
itjc_xdu(struct l1_softc *sc, l1_bchan_state_t *chan, dma_tx_context_t *ctx,
u_int16_t *dst_p, u_int16_t *dst_end_p, u_int8_t tx_restart)
{
	u_int8_t	xdu;

	u_int16_t	dst_end,
			dst,
			dma,
			dma_l,
			dma_h,
			xdu_l,
			xdu_h;

	itjc_bus_setup(sc);

	/*
	 * Since the hardware is running, be conservative and assume
	 * the pointer location has a `fuzy' error factor.
	 */
	dma   = itjc_get_dma_offset(ctx, TIGER_DMA_WR_CURR_ADDR);
	dma_l = dma;
	dma_h = itjc_ring_add(dma, 1);

	dst_end = itjc_ring_sub(dma_l, ITJC_RING_SLOT_WORDS);

	if (ctx->state != ITJC_TS_ACTIVE)
	{
		xdu = (ctx->state == ITJC_TS_AFTER_XDU);
		dst = itjc_ring_add(dma_h, 4);
		goto done;
	}

	/*
	 * Check for xmit underruns.
	 */
	xdu_l = dst = ctx->next_write; 
	xdu_h = itjc_ring_add(dst, ITJC_RING_SLOT_WORDS);

	if (xdu_l < xdu_h)
		xdu =	   (xdu_l <= dma_l && dma_l < xdu_h)
			|| (xdu_l <= dma_h && dma_h < xdu_h);
	else
		xdu =	   (xdu_l <= dma_l || dma_l < xdu_h)
			|| (xdu_l <= dma_h || dma_h < xdu_h);

	if (xdu)
	{
		ctx->state = ITJC_TS_AFTER_XDU;

		dst = itjc_ring_add(dma_h, 4);
	}
	else if (tx_restart)
	{
		/*
		 * See if we still can restart from immediately
		 * after the last frame sent. It's a XDU test but
		 * using the real data end on the comparsions. We
		 * don't consider XDU an error here because we were
		 * just trying to avoid send a filling gap between
		 * frames. If it's already sent no harm is done.
		 */
		xdu_l = dst = ctx->next_frame; 
		xdu_h = itjc_ring_add(dst, ITJC_RING_SLOT_WORDS);

		if (xdu_l < xdu_h)
			xdu =	   (xdu_l <= dma_l && dma_l < xdu_h)
				|| (xdu_l <= dma_h && dma_h < xdu_h);
		else
			xdu =	   (xdu_l <= dma_l || dma_l < xdu_h)
				|| (xdu_l <= dma_h || dma_h < xdu_h);

		if (xdu)
			dst = itjc_ring_add(dma_h, 4);

		xdu = 0;
	}

done:
	if (dst_p != NULL)
		*dst_p = dst;
	
	if (dst_end_p != NULL)
		*dst_end_p = dst_end;

	ctx->next_write = dst_end;

	return xdu;
}


#define itjc_rotate_hdlc_flag(blevel)					\
	((u_int8_t)(0x7E7E >> (8 - (u_int8_t)((blevel) >> 8))))


static void
itjc_dma_rx_intr(struct l1_softc *sc, l1_bchan_state_t *chan,
dma_rx_context_t *ctx)
{
	u_int8_t	*ring,
			*dst,
			*dst_end,
			flag,
			blevel;

	u_int16_t	dma,
			src,
			tmp2,
			tmp,
			len,
			crc,
			ib;
	
	itjc_bus_setup(sc);


	if (ctx->state == ITJC_RS_IDLE)
		return;

	ring = ctx->ring;
	dma = itjc_get_dma_offset(ctx, TIGER_DMA_RD_CURR_ADDR);
	dma = itjc_ring_sub(dma, 1);
	src = ctx->next_read;

	if (chan->bprot == BPROT_NONE)
	{
		dst = itjc_get_rx_mbuf(chan, &dst_end, ITJC_MB_CURR);

		while (src != dma)
		{
			if (dst == NULL)
				dst = itjc_get_rx_mbuf(chan, &dst_end, 
					ITJC_MB_NEW);

			*dst++ = ring[src];
			src = itjc_ring_add(src, 1);

			if (dst >= dst_end)
			{
				itjc_put_rx_mbuf(sc, chan, ITJC_MAX_RAW_LEN);
				dst = dst_end = NULL;
			}
		}
		ctx->next_read = src;
		itjc_save_rx_mbuf(chan, dst);
		return;
	}

	blevel = ctx->hdlc_blevel;
	flag   = ctx->hdlc_flag;
	len    = ctx->hdlc_len;
	tmp    = ctx->hdlc_tmp;
	crc    = ctx->hdlc_crc;
	ib     = ctx->hdlc_ib;

	dst = itjc_get_rx_mbuf(chan, NULL, ITJC_MB_CURR);

	while (src != dma)
	{
		HDLC_DECODE(*dst++, len, tmp, tmp2, blevel, ib, crc, flag,
		{/* rdd */
			tmp2 = ring[src];
			src = itjc_ring_add(src, 1);
		},
		{/* nfr */
			if (dst != NULL)
				panic("itjc_dma_rx_intr: nfrcmd with "
					"valid current frame");

			dst = itjc_get_rx_mbuf(chan, &dst_end, ITJC_MB_NEW);
			len = dst_end - dst;
		},
		{/* cfr */
			len = ITJC_MAX_HDLC_LEN - len;

			if ((!len) || (len > ITJC_MAX_HDLC_LEN))
			{
				/*
				 * NOTE: frames without any data, only crc
				 * field, should be silently discared.
				 */
				NDBGL1(L1_S_MSG, "itjc_dma_rx_intr: "
					"bad frame (len=%d, unit=%d)",
					len, sc->sc_unit);

				itjc_free_rx_mbuf(chan);

				goto s0;
			}

			if (crc)
			{
				NDBGL1(L1_S_ERR,
					"CRC (crc=0x%04x, len=%d, unit=%d)",
					crc, len, sc->sc_unit);

				itjc_free_rx_mbuf(chan);

				goto s0;
			}

			itjc_put_rx_mbuf(sc, chan, len);

		s0:
			dst = NULL;
			len = 0;
		},
		{/* rab */
			NDBGL1(L1_S_ERR, "Read Abort (unit=%d)", sc->sc_unit);

			itjc_free_rx_mbuf(chan);
			dst = NULL;
			len = 0;
		},
		{/* rdo */
			NDBGL1(L1_S_ERR, "RDO (unit=%d) dma=%d src=%d",
				sc->sc_unit, dma, src);

			itjc_free_rx_mbuf(chan);
			dst = NULL;
			len = 0;
		},
		continue,
		d);
	}

	itjc_save_rx_mbuf(chan, dst);

	ctx->next_read	= src;
	ctx->hdlc_blevel= blevel;
	ctx->hdlc_flag	= flag;
	ctx->hdlc_len	= len;
	ctx->hdlc_tmp	= tmp;
	ctx->hdlc_crc	= crc;
	ctx->hdlc_ib	= ib;
}


/*
 * The HDLC side of itjc_dma_tx_intr. We made a separate function
 * to improve readability and (perhaps) help the compiler with
 * register allocation.
 */
static void
itjc_hdlc_encode(struct l1_softc *sc, l1_bchan_state_t *chan,
dma_tx_context_t * ctx)
{
	u_int8_t	*ring,
			*src,
			xdu,
			flag,
			flag_byte,
			tx_restart;

	u_int16_t	saved_len,
			dst_end,
			dst_end1,
			dst,
			filled,
			blevel,
			tmp2,
			len,
			crc,
			ib;

	u_int32_t	tmp;


	saved_len = len = itjc_get_tx_mbuf(sc, chan, &src, ITJC_MB_CURR);

	filled = ctx->filled;
	flag   = ctx->hdlc_flag;

	if (src == NULL && flag == 2 && filled >= ITJC_RING_WORDS)
		return;

	tx_restart = (flag == 2 && src != NULL);
	xdu = itjc_xdu(sc, chan, ctx, &dst, &dst_end, tx_restart);

	ring   = ctx->ring;

	ib     = ctx->hdlc_ib;
	crc    = ctx->hdlc_crc;
	tmp    = ctx->hdlc_tmp;
	blevel = ctx->hdlc_blevel;

	if (xdu)
	{
		if (flag != 2)
		{
			NDBGL1(L1_H_XFRERR, "XDU");
			++chan->stat_XDU;

			/*
			 * Abort the current frame and 
			 * prepare for a full restart.
			 */
			itjc_free_tx_mbufs(chan);
			saved_len = len = filled = 0;
			flag = (u_int8_t)-2;
		}
		else if (filled < ITJC_RING_SLOT_WORDS)
		{
			/*
			 * A little garbage may have been retransmitted.
			 * Send an abort before any new data.
			 */
			filled = 0;
			flag = (u_int8_t)-2;
		}
	}

	if (flag != 3)
		len = 0;

	while (dst != dst_end)
	{
		HDLC_ENCODE(
		*src++, len, tmp, tmp2, blevel, ib, crc, flag,
		{/* gfr */
			if ((len = saved_len) == 0)
				len = itjc_get_tx_mbuf(sc, chan, &src,
					ITJC_MB_NEW);

			if (len == 0)
			{
				ctx->next_frame = dst;

				flag_byte = itjc_rotate_hdlc_flag(blevel);

				for (dst_end1 = itjc_ring_sub(dst_end, 1);
				dst != dst_end1;
				dst = itjc_ring_add(dst, 1))
				{
					ring[dst] = flag_byte;
					++filled;
				}
			}
			else
				filled = 0;

			ctx->state = ITJC_TS_ACTIVE;
		},
		{/* nmb */
			saved_len = 0;
			len = itjc_get_tx_mbuf(sc, chan, &src, ITJC_MB_NEXT);
		},
		{/* wrd */
			ring[dst] = (u_int8_t)tmp;
			dst = itjc_ring_add(dst, 1);
		},
		d1);
	}

	ctx->hdlc_blevel = blevel;
	ctx->hdlc_flag   = flag;
	ctx->hdlc_tmp    = tmp;
	ctx->hdlc_crc    = crc;
	ctx->hdlc_ib     = ib;

	ctx->filled = filled;
	ctx->next_write = dst;

	itjc_save_tx_mbuf(chan, src, len);
}


static void
itjc_dma_tx_intr(struct l1_softc *sc, l1_bchan_state_t *chan,
dma_tx_context_t * ctx)
{
	u_int8_t	*data_end,
			*ring,
			*src,
			xdu;

	u_int16_t	dst,
			dst_end,
			filled,
			len;


	if (ctx->state == ITJC_TS_IDLE)
		goto done;

	if (chan->bprot != BPROT_NONE)
	{
		itjc_hdlc_encode(sc, chan, ctx);
		goto done;
	}

	ring   = ctx->ring;
	filled = ctx->filled;

	len = itjc_get_tx_mbuf(sc, chan, &src, ITJC_MB_CURR);

	if (len == 0 && filled >= ITJC_RING_WORDS)
		goto done;

	xdu = itjc_xdu(sc, chan, ctx, &dst, &dst_end, len != 0);

	if (xdu && filled < ITJC_RING_WORDS)
	{
		NDBGL1(L1_H_XFRERR, "XDU");
		++chan->stat_XDU;
		filled = 0;
	}

	if (len == 0)
		goto fill_ring;

	ctx->state = ITJC_TS_ACTIVE;

	data_end = src + len;
	while (dst != dst_end)
	{
		ring[dst] = *src++; --len;

		dst = itjc_ring_add(dst, 1);

		if (src >= data_end)
		{
			len = itjc_get_tx_mbuf(sc, chan, &src, ITJC_MB_NEXT);
			if (len == 0)
				len = itjc_get_tx_mbuf(sc, chan,
					 &src, ITJC_MB_NEW);

			if (len == 0)
			{
				data_end = NULL;
				break;
			}
			data_end = src + len;
		}
	}

	itjc_save_tx_mbuf(chan, src, len);

	filled = 0;

fill_ring:
	ctx->next_frame = dst;

	for (; dst != dst_end; dst = itjc_ring_add(dst, 1))
	{
		ring[dst] = ITJC_TEL_SILENCE_BYTE;
		++filled;
	}

	ctx->next_write = dst;
	ctx->filled = filled;

done:
}


/*---------------------------------------------------------------------------*
 *	NetJet fifo read/write routines.
 *---------------------------------------------------------------------------*/

static void
itjc_read_fifo(struct l1_softc *sc, int what, void *buf, size_t size)
{
	itjc_bus_setup(sc);

	if (what != ISIC_WHAT_ISAC)
		panic("itjc_write_fifo: Trying to read from HSCX fifo.\n");

	itjc_set_pib_addr_msb(0);
	itjc_read_multi_1(PIB_OFFSET, buf, size);
}


static void
itjc_write_fifo(struct l1_softc *sc, int what, void *buf, size_t size)
{
	itjc_bus_setup(sc);

	if (what != ISIC_WHAT_ISAC)
		panic("itjc_write_fifo: Trying to write to HSCX fifo.\n");

	itjc_set_pib_addr_msb(0);
	itjc_write_multi_1(PIB_OFFSET, buf, size);
}


/*---------------------------------------------------------------------------*
 *	Read an ISAC register.
 *---------------------------------------------------------------------------*/
static u_int8_t
itjc_read_reg(struct l1_softc *sc, int what, bus_size_t offs)
{
	itjc_bus_setup(sc);

	if (what != ISIC_WHAT_ISAC)
	{
		panic("itjc_read_reg: what(%d) != ISIC_WHAT_ISAC\n",
			what);
		return 0;
	}

	itjc_set_pib_addr_msb(offs);
	return itjc_read_1(itjc_pib_2_pci(offs));
}


/*---------------------------------------------------------------------------*
 *	Write an ISAC register.
 *---------------------------------------------------------------------------*/
static void
itjc_write_reg(struct l1_softc *sc, int what, bus_size_t offs, u_int8_t data)
{
	itjc_bus_setup(sc);

	if (what != ISIC_WHAT_ISAC)
	{
		panic("itjc_write_reg: what(%d) != ISIC_WHAT_ISAC\n",
			what);
		return;
	}

	itjc_set_pib_addr_msb(offs);
	itjc_write_1(itjc_pib_2_pci(offs), data);
}


/*---------------------------------------------------------------------------*
 *	itjc_probe - probe for a card.
 *---------------------------------------------------------------------------*/
static int itjc_probe(device_t dev)
{
	u_int16_t	vid = pci_get_vendor(dev),
			did = pci_get_device(dev);

	if ((vid == PCI_TJNET_VID) && (did == PCI_TJ300_DID))
	{
		device_set_desc(dev, "NetJet-S");
		return 0;
	}

	return ENXIO;
}


/*---------------------------------------------------------------------------*
 *	itjc_attach - attach a (previously probed) card.
 *---------------------------------------------------------------------------*/
int
itjc_attach(device_t dev)
{
	bus_space_handle_t	h;
	bus_space_tag_t		t; 

	struct l1_softc		*sc = device_get_softc(dev);

	u_int16_t		vid = pci_get_vendor(dev),
				did = pci_get_device(dev);

	int			unit = device_get_unit(dev),
				s = splimp(),
				res_init_level = 0,
				error = 0;

	void			*ih = 0;

	dma_context_t		*ctx = &dma_context[unit];

	bzero(sc, sizeof(struct l1_softc));

	/* Probably not really required. */
	if (unit > ITJC_MAXUNIT)
	{
		printf("itjc%d: Error, unit > ITJC_MAXUNIT!\n", unit);
		splx(s);
		return ENXIO;
	}

	if (!(vid == PCI_TJNET_VID && did == PCI_TJ300_DID))
	{
		printf("itjc%d: unknown device (%04X,%04X)!\n", unit, vid, did);
		goto fail;
	}

	itjc_scp[unit] = sc;

	sc->sc_resources.io_rid[0] = PCIR_MAPS+0;
	sc->sc_resources.io_base[0] = bus_alloc_resource(dev, SYS_RES_IOPORT,
		&sc->sc_resources.io_rid[0], 0, ~0, 1, RF_ACTIVE);

	if (sc->sc_resources.io_base[0] == NULL)
	{
		printf("itjc%d: couldn't map IO port\n", unit);
		error = ENXIO;
		goto fail;
	}

	h = rman_get_bushandle(sc->sc_resources.io_base[0]);
	t = rman_get_bustag(sc->sc_resources.io_base[0]); 

	++res_init_level;

	/* Allocate interrupt. */
	sc->sc_resources.irq_rid = 0;
	sc->sc_resources.irq = bus_alloc_resource(dev, SYS_RES_IRQ,
		&sc->sc_resources.irq_rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);

	if (sc->sc_resources.irq == NULL)
	{
		printf("itjc%d: couldn't map interrupt\n", unit);
		error = ENXIO;
		goto fail;
	}

	++res_init_level;

	error = bus_setup_intr(dev, sc->sc_resources.irq, INTR_TYPE_NET,
			 itjc_intr, sc, &ih);

	if (error)
	{
		printf("itjc%d: couldn't set up irq handler\n", unit);
		error = ENXIO;
		goto fail;
	}

	/*
	 * Reset the ASIC & the ISAC.
	 */
	itjc_write_1(TIGER_RESET_PIB_CL_TIME, TIGER_RESET_ALL);

	DELAY(SEC_DELAY/100); /* Give it 10 ms to reset ...*/

	itjc_write_1(TIGER_RESET_PIB_CL_TIME,
		TIGER_SELF_ADDR_DMA | TIGER_PIB_3_CYCLES);

	DELAY(SEC_DELAY/100); /* ... and more 10 to recover. */

	/*
	 * First part of DMA initialization. Create & map the memory
	 * pool that will be used to bear the rx & tx ring buffers.
	 */
	ctx->state = ITJC_DS_LOADING;

	error = bus_dma_tag_create(
		NULL,					/* parent */
		4,					/* alignment*/
		0,					/* boundary*/
		BUS_SPACE_MAXADDR_32BIT,		/* lowaddr*/	
		BUS_SPACE_MAXADDR,			/* highaddr*/
		NULL,					/* filter*/
		NULL,					/* filterarg*/
		ITJC_DMA_POOL_BYTES,			/* maxsize*/
		1,					/* nsegments*/
		ITJC_DMA_POOL_BYTES,			/* maxsegsz*/
		BUS_DMA_ALLOCNOW | BUS_DMAMEM_NOSYNC,	/* flags*/
		&ctx->tag);

	if (error)
	{
		printf("itjc%d: couldn't create bus DMA tag.\n", unit);
		goto fail;
	}

	++res_init_level;

        error = bus_dmamem_alloc(
		ctx->tag, 				/* DMA tag */
		(void **)&ctx->pool,	/* KV addr of the allocated memory */
		BUS_DMA_NOWAIT | BUS_DMAMEM_NOSYNC,	/* flags */
		&ctx->map);				/* KV <-> PCI map */

	if (error)
                goto fail;

        /*
	 * Load the KV <-> PCI map so the device sees the same
	 * memory segment as pointed by pool. Note: since the
	 * load may happen assyncronously (completion indicated by
	 * the execution of the callback function) we have to
	 * delay the initialization of the DMA engine to a moment we
	 * actually have the proper bus addresses to feed the Tiger
	 * and our DMA control blocks. This will be done in
	 * itjc_bchannel_setup via a call to itjc_dma_start.
	 */
        bus_dmamap_load(
		ctx->tag,		/* DMA tag */
		ctx->map,		/* DMA map */
		ctx->pool,		/* KV addr of buffer */
		ITJC_DMA_POOL_BYTES,	/* buffer size */
		itjc_map_callback, 	/* this receive the bus addr/error */
		ctx, 			/* callback aux arg */
		0);			/* flags */

	++res_init_level;

	/*
	 * Setup the AUX port so we can talk to the ISAC.
	 */
	itjc_write_1(TIGER_AUX_PORT_CNTL, TIGER_AUX_NJ_DEFAULT);
	itjc_write_1(TIGER_INT1_MASK, TIGER_ISAC_INT);

	/*
	 * From now on, almost like a `normal' ISIC driver.
	 */

	sc->sc_unit = unit;

	ISAC_BASE = (caddr_t)ISIC_WHAT_ISAC;

	HSCX_A_BASE = (caddr_t)ISIC_WHAT_HSCXA;
	HSCX_B_BASE = (caddr_t)ISIC_WHAT_HSCXB;

	/* setup access routines */

	sc->clearirq = NULL;
	sc->readreg = itjc_read_reg;
	sc->writereg = itjc_write_reg;

	sc->readfifo = itjc_read_fifo;
	sc->writefifo = itjc_write_fifo;

	/* setup card type */
	
	sc->sc_cardtyp = CARD_TYPEP_NETJET_S;

	/* setup IOM bus type */
	
	sc->sc_bustyp = BUS_TYPE_IOM2;

	/* set up some other miscellaneous things */
	sc->sc_ipac = 0;
	sc->sc_bfifolen = 2 * ITJC_RING_SLOT_WORDS;

	printf("itjc%d: ISAC 2186 Version 1.1 (IOM-2)\n", unit);

	/* init the ISAC */
	itjc_isac_init(sc);

	/* init the "HSCX" */
	itjc_bchannel_setup(sc->sc_unit, HSCX_CH_A, BPROT_NONE, 0);
	
	itjc_bchannel_setup(sc->sc_unit, HSCX_CH_B, BPROT_NONE, 0);

	/* can't use the normal B-Channel stuff */
	itjc_init_linktab(sc);

	/* set trace level */

	sc->sc_trace = TRACE_OFF;

	sc->sc_state = ISAC_IDLE;

	sc->sc_ibuf = NULL;
	sc->sc_ib = NULL;
	sc->sc_ilen = 0;

	sc->sc_obuf = NULL;
	sc->sc_op = NULL;
	sc->sc_ol = 0;
	sc->sc_freeflag = 0;

	sc->sc_obuf2 = NULL;
	sc->sc_freeflag2 = 0;

#if defined(__FreeBSD__) && __FreeBSD__ >=3
	callout_handle_init(&sc->sc_T3_callout);
	callout_handle_init(&sc->sc_T4_callout);	
#endif
	
	/* init higher protocol layers */
	
	i4b_l1_mph_status_ind(L0ITJCUNIT(sc->sc_unit), STI_ATTACH, 
		sc->sc_cardtyp, &itjc_l1mux_func);

	splx(s);
	return 0;

  fail:
	switch (res_init_level)
	{
	case 5:
		bus_dmamap_unload(ctx->tag, ctx->map);
		/* FALL TRHU */

	case 4:
		bus_dmamem_free(ctx->tag, ctx->pool, ctx->map);
		bus_dmamap_destroy(ctx->tag, ctx->map);
		/* FALL TRHU */

	case 3:
		bus_dma_tag_destroy(ctx->tag);
		/* FALL TRHU */

	case 2:
		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_resources.irq);
		/* FALL TRHU */

	case 1:
		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS+0,
			sc->sc_resources.io_base[0]);
		/* FALL TRHU */

	case 0:
	}

	itjc_scp[unit] = NULL;

	splx(s);
	return error;
}


/*---------------------------------------------------------------------------*
 *	itjc_intr - main interrupt service routine.
 *---------------------------------------------------------------------------*/
static void
itjc_intr(void *xsc)
{
	struct l1_softc		*sc	= xsc;
	l1_bchan_state_t	*chan	= &sc->sc_chan[0];
	dma_context_t		*dma	= &dma_context[sc->sc_unit];
	dma_rx_context_t	*rxc	= &dma_rx_context[sc->sc_unit][0];
	dma_tx_context_t	*txc	= &dma_tx_context[sc->sc_unit][0];

	itjc_bus_setup(sc);

	/* Honor interrupts from successfully configured cards only. */
	if (dma->state < ITJC_DS_STOPPED)
		return;

	/* First, we check the ISAC... */
	if (! (itjc_read_1(TIGER_AUX_PORT_DATA) & TIGER_ISAC_INT_MASK))
	{
		itjc_write_1(TIGER_INT1_STATUS, TIGER_ISAC_INT);
		NDBGL1(L1_H_IRQ, "ISAC");
		itjc_isac_intr(sc);
	}

	/* ... after what we always have a look at the DMA rings. */

	NDBGL1(L1_H_IRQ, "Tiger");

	itjc_read_1(TIGER_INT0_STATUS);
	itjc_write_1(TIGER_INT0_STATUS, TIGER_TARGET_ABORT_INT
		| TIGER_MASTER_ABORT_INT | TIGER_RD_END_INT
		| TIGER_RD_INT_INT	 | TIGER_WR_END_INT | TIGER_WR_INT_INT);

	itjc_dma_rx_intr(sc, chan, rxc);
	itjc_dma_tx_intr(sc, chan, txc);

	++chan; ++rxc; ++txc;

	itjc_dma_rx_intr(sc, chan, rxc);
	itjc_dma_tx_intr(sc, chan, txc);
}


/*---------------------------------------------------------------------------*
 *	itjc_bchannel_setup - (Re)initialize and start/stop a Bchannel.
 *---------------------------------------------------------------------------*/
static void
itjc_bchannel_setup(int unit, int h_chan, int bprot, int activate)
{
#ifdef __FreeBSD__
	struct l1_softc		*sc	= itjc_scp[unit];
#else
	struct l1_softc		*sc	= isic_find_sc(unit);
#endif
	l1_bchan_state_t	*chan	= &sc->sc_chan[h_chan];
	int			s	= SPLI4B();
		
	NDBGL1(L1_BCHAN, "unit=%d, channel=%d, %s",
		unit, h_chan, activate ? "activate" : "deactivate");

	/*
	 * If we are deactivating the channel, we have to stop
	 * the DMA before we reset the channel control structures.
	 */
	if (! activate)
		itjc_bchannel_dma_setup(sc, h_chan, activate); 

	/* general part */

	chan->state = HSCX_IDLE;

	chan->unit = sc->sc_unit;	/* unit number */
	chan->channel = h_chan;		/* B channel */
	chan->bprot = bprot;		/* B channel protocol */

	/* receiver part */

	i4b_Bcleanifq(&chan->rx_queue);	/* clean rx queue */

	chan->rx_queue.ifq_maxlen = IFQ_MAXLEN;

	chan->rxcount = 0;		/* reset rx counter */
	
	i4b_Bfreembuf(chan->in_mbuf);	/* clean rx mbuf */

	chan->in_mbuf = NULL;		/* reset mbuf ptr */
	chan->in_cbptr = NULL;		/* reset mbuf curr ptr */
	chan->in_len = 0;		/* reset mbuf data len */
	
	/* transmitter part */

	i4b_Bcleanifq(&chan->tx_queue);	/* clean tx queue */

	chan->tx_queue.ifq_maxlen = IFQ_MAXLEN;
	
	chan->txcount = 0;		/* reset tx counter */
	
	i4b_Bfreembuf(chan->out_mbuf_head);	/* clean tx mbuf */

	chan->out_mbuf_head = NULL;	/* reset head mbuf ptr */
	chan->out_mbuf_cur = NULL;	/* reset current mbuf ptr */	
	chan->out_mbuf_cur_ptr = NULL;	/* reset current mbuf data ptr */
	chan->out_mbuf_cur_len = 0;	/* reset current mbuf data cnt */

	/*
	 * Only setup & start the DMA after all other channel
	 * control structures are in place.
	 */
	if (activate)
		itjc_bchannel_dma_setup(sc, h_chan, activate); 

	splx(s);
}


/*---------------------------------------------------------------------------*
 *	itjc_bchannel_start - Signal us we have more data to send.
 *---------------------------------------------------------------------------*/
static void
itjc_bchannel_start(int unit, int h_chan)
{
#if Buggy_code
	/*
	 * I disabled this routine because it was causing crashes when
	 * this driver was used with the ISP (kernel SPPP) protocol driver.
	 * The scenario is reproductible:
	 *	Use the -link1 (dial on demand) ifconfig option.
	 *	Start an interactive  TCP connection to somewhere.
	 *	Wait until the PPP connection times out and is dropped.
	 *	Try to send something on the TCP connection.
	 *	The machine will print some garbage and halt or reboot
	 *	(no panic messages).
	 *
	 * I've nailed down the problem to the fact that this routine
	 * was being called before the B channel had been setup again.
	 *
	 * For now, I don't have a good solution other than this one.
	 * But, don't despair. The impact of it is unnoticeable.
	 */

#ifdef __FreeBSD__
	struct l1_softc  *sc	= itjc_scp[unit];
#else
	struct l1_softc	 *sc	= isic_find_sc(unit);
#endif
	l1_bchan_state_t *chan	= &sc->sc_chan[h_chan];

	int		 s	= SPLI4B();

	dma_tx_context_t *txc	= &dma_tx_context[unit][h_chan];

	if (chan->state & HSCX_TX_ACTIVE)
	{
		splx(s);
		return;
	}

	itjc_dma_tx_intr(sc, chan, txc);

	splx(s);
#endif
}


/*---------------------------------------------------------------------------*
 *	itjc_shutdown - Stop the driver and reset the card.
 *---------------------------------------------------------------------------*/
static void
itjc_shutdown(device_t dev)
{
	struct l1_softc *sc = device_get_softc(dev);

	itjc_bus_setup(sc);

	/*
	 * Stop the DMA the nice and easy way.
	 */
	itjc_bchannel_setup(sc->sc_unit, 0, BPROT_NONE, 0);
	itjc_bchannel_setup(sc->sc_unit, 1, BPROT_NONE, 0);

	/*
	 * Reset the card.
	 */
	itjc_write_1(TIGER_RESET_PIB_CL_TIME, TIGER_RESET_ALL);

	DELAY(SEC_DELAY/100); /* Give it 10 ms to reset ...*/

	itjc_write_1(TIGER_RESET_PIB_CL_TIME,
		TIGER_SELF_ADDR_DMA | TIGER_LATCH_DMA_INT | TIGER_PIB_3_CYCLES);

	DELAY(SEC_DELAY/100); /* ... and more 10 to recover */
}


/*---------------------------------------------------------------------------*
 *	itjc_ret_linktab - Return the address of itjc drivers linktab.
 *---------------------------------------------------------------------------*/
isdn_link_t *
itjc_ret_linktab(int unit, int channel)
{
#ifdef __FreeBSD__
	struct l1_softc		*sc = itjc_scp[unit];
#else
	struct l1_softc		*sc = isic_find_sc(unit);
#endif
	l1_bchan_state_t	*chan = &sc->sc_chan[channel];

	return(&chan->isic_isdn_linktab);
}
 
/*---------------------------------------------------------------------------*
 *	itjc_set_linktab - Set the driver linktab in the b channel softc.
 *---------------------------------------------------------------------------*/
void
itjc_set_linktab(int unit, int channel, drvr_link_t *dlt)
{
#ifdef __FreeBSD__
	struct l1_softc *sc	= itjc_scp[unit];
#else
	struct l1_softc *sc	= isic_find_sc(unit);
#endif
	l1_bchan_state_t *chan	= &sc->sc_chan[channel];

	chan->isic_drvr_linktab = dlt;
}


/*---------------------------------------------------------------------------*
 *	itjc_init_linktab - Initialize our local linktab.
 *---------------------------------------------------------------------------*/
static void
itjc_init_linktab(struct l1_softc *sc)
{
	l1_bchan_state_t *chan = &sc->sc_chan[HSCX_CH_A];
	isdn_link_t *lt = &chan->isic_isdn_linktab;

	/* make sure the hardware driver is known to layer 4 */
	/* avoid overwriting if already set */
	if (ctrl_types[CTRL_PASSIVE].set_linktab == NULL)
	{
		ctrl_types[CTRL_PASSIVE].set_linktab = itjc_set_linktab;
		ctrl_types[CTRL_PASSIVE].get_linktab = itjc_ret_linktab;
	}

	/* local setup */
	lt->unit = sc->sc_unit;
	lt->channel = HSCX_CH_A;
	lt->bch_config = itjc_bchannel_setup;
	lt->bch_tx_start = itjc_bchannel_start;
	lt->bch_stat = itjc_bchannel_stat;
	lt->tx_queue = &chan->tx_queue;

	/* used by non-HDLC data transfers, i.e. telephony drivers */
	lt->rx_queue = &chan->rx_queue;

	/* used by HDLC data transfers, i.e. ipr and isp drivers */	
	lt->rx_mbuf = &chan->in_mbuf;	
                                                
	chan = &sc->sc_chan[HSCX_CH_B];
	lt = &chan->isic_isdn_linktab;

	lt->unit = sc->sc_unit;
	lt->channel = HSCX_CH_B;
	lt->bch_config = itjc_bchannel_setup;
	lt->bch_tx_start = itjc_bchannel_start;
	lt->bch_stat = itjc_bchannel_stat;
	lt->tx_queue = &chan->tx_queue;

	/* used by non-HDLC data transfers, i.e. telephony drivers */
	lt->rx_queue = &chan->rx_queue;

	/* used by HDLC data transfers, i.e. ipr and isp drivers */	
	lt->rx_mbuf = &chan->in_mbuf;	
}


/*---------------------------------------------------------------------------*
 *	itjc_bchannel_stat - Collect link statistics for a given B channel.
 *---------------------------------------------------------------------------*/
static void
itjc_bchannel_stat(int unit, int h_chan, bchan_statistics_t *bsp)
{
#ifdef __FreeBSD__
	struct l1_softc *sc = itjc_scp[unit];
#else
	struct l1_softc *sc = isic_find_sc(unit);
#endif
	l1_bchan_state_t *chan = &sc->sc_chan[h_chan];
	int s;

	s = SPLI4B();
	
	bsp->outbytes = chan->txcount;
	bsp->inbytes = chan->rxcount;

	chan->txcount = 0;
	chan->rxcount = 0;

	splx(s);
}


/*---------------------------------------------------------------------------*
 *	Netjet - ISAC interrupt routine.
 *---------------------------------------------------------------------------*/
static void
itjc_isac_intr(struct l1_softc *sc)
{
	register u_char irq_stat;

	do
	{
		/* get isac irq status */
		irq_stat = ISAC_READ(I_ISTA);

		if(irq_stat)
			itjc_isac_irq(sc, irq_stat); /* isac handler */
	}
	while(irq_stat);

	ISAC_WRITE(I_MASK, 0xff);

	DELAY(100);

	ISAC_WRITE(I_MASK, ISAC_IMASK);
}


/*---------------------------------------------------------------------------*
 *	itjc_recover - Try to recover from ISAC irq lockup.
 *---------------------------------------------------------------------------*/
void
itjc_recover(struct l1_softc *sc)
{
	u_char byte;
	
	/* get isac irq status */

	byte = ISAC_READ(I_ISTA);

	NDBGL1(L1_ERROR, "  ISAC: ISTA = 0x%x", byte);
	
	if(byte & ISAC_ISTA_EXI)
		NDBGL1(L1_ERROR, "  ISAC: EXIR = 0x%x", (u_char)ISAC_READ(I_EXIR));

	if(byte & ISAC_ISTA_CISQ)
	{
		byte = ISAC_READ(I_CIRR);
	
		NDBGL1(L1_ERROR, "  ISAC: CISQ = 0x%x", byte);
		
		if(byte & ISAC_CIRR_SQC)
			NDBGL1(L1_ERROR, "  ISAC: SQRR = 0x%x", (u_char)ISAC_READ(I_SQRR));
	}

	NDBGL1(L1_ERROR, "  ISAC: IMASK = 0x%x", ISAC_IMASK);

	ISAC_WRITE(I_MASK, 0xff);	
	DELAY(100);
	ISAC_WRITE(I_MASK, ISAC_IMASK);
}

#endif /* NITJC > 0 */

--LZvS9be/3tNcYl/X--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-isdn" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010103150832.A19526>