/*-
 * Copyright (c) 2000 Hans Petter Selasky. 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.
 *
 * 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_ihfc_drv.c - ihfc ISA PnP-bus interface
 *	-------------------------------------------
 *	Everything which has got anything to do with the
 *	HFC-1/S/SP chips has been put here.
 *
 *	last edit-date: [Fri Jan 12 17:06:52 2001]
 *
 *---------------------------------------------------------------------------*/

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/i4b/layer1/ihfc/i4b_ihfc_drv.c,v 1.16 2005/01/06 22:18:19 imp Exp $");

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

#include <sys/mbuf.h>

#include <i4b/include/i4b_mbuf.h>

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

#include <i4b/layer1/i4b_l1.h>
#include <i4b/layer1/i4b_hdlc.h>
#include <i4b/layer1/ihfc/i4b_ihfc.h>
#include <i4b/layer1/ihfc/i4b_ihfc_ext.h>
#include <i4b/layer1/ihfc/i4b_ihfc_drv.h>

#include <machine/bus.h>
#include <sys/rman.h>


/*---------------------------------------------------------------------------*
 *	Local prototypes
 *---------------------------------------------------------------------------*/
	void 	ihfc_loadconfig   (ihfc_sc_t *sc);

static  void	ihfc_trans_Bread  (ihfc_sc_t *sc, u_char chan);
static  void	ihfc_trans_Bwrite (ihfc_sc_t *sc, u_char chan);
static 	void	ihfc_hdlc_Bread   (ihfc_sc_t *sc, u_char chan);
static	void	ihfc_hdlc_Bwrite  (ihfc_sc_t *sc, u_char chan);
static  void	ihfc_hdlc_Dread   (ihfc_sc_t *sc, u_char chan);
static  void	ihfc_hdlc_Dwrite  (ihfc_sc_t *sc, u_char chan);

static  void	ihfc_isac_Dread   (ihfc_sc_t *sc, u_char chan);
static  void	ihfc_isac_Dwrite  (ihfc_sc_t *sc, u_char chan);

	void	ihfc_cmdr_hdlr 	  (ihfc_sc_t *sc, u_char cmdr);
	void	ihfc_exir_hdlr 	  (ihfc_sc_t *sc, u_char exir);

	void	ihfc_sq		  (ihfc_sc_t *sc);

static	void	ihfc_test_Bread	  (ihfc_sc_t *sc, u_char chan);
static 	void	ihfc_test_Bwrite  (ihfc_sc_t *sc, u_char chan);

u_short		ihfc_Bsel_fifo	  (ihfc_sc_t *sc, u_char chan, u_char flag);
u_int32_t	ihfc_Dsel_fifo	  (ihfc_sc_t *sc, u_char chan, u_char flag);


/*---------------------------------------------------------------------------*
 *	Commonly used ISA bus commands
 *---------------------------------------------------------------------------*/
#define IHFC_DATA_OFFSET	0
#define IHFC_REG_OFFSET		1

#define BUS_VAR			bus_space_handle_t h = rman_get_bushandle(S_IOBASE[0]); \
				bus_space_tag_t    t = rman_get_bustag   (S_IOBASE[0])

#define SET_REG(reg)		bus_space_write_1(t,h, IHFC_REG_OFFSET, reg)
#define GET_STAT		bus_space_read_1 (t,h, IHFC_REG_OFFSET)

#define READ_DATA_1		bus_space_read_1 (t,h, IHFC_DATA_OFFSET)
#define READ_BOTH_2		bus_space_read_2 (t,h, IHFC_DATA_OFFSET)

#define	WRITE_DATA_1(data)	bus_space_write_1(t,h, IHFC_DATA_OFFSET, data)
#define WRITE_BOTH_2(data)	bus_space_write_2(t,h, IHFC_DATA_OFFSET, data)

#define DISBUSY(okcmd, tocmd)						\
{									\
	if (GET_STAT & 1)						\
	{								\
		register u_char a;					\
		register u_int to = IHFC_DISBUSYTO;			\
									\
		while(((a = GET_STAT) & 1) && --to);			\
									\
		if (!to)						\
		{							\
			NDBGL1(L1_ERROR, "DISBUSY-TIMEOUT! (a=%04x, "	\
			"unit=%d)", a, S_UNIT);				\
			tocmd;						\
		}							\
		else							\
		{							\
			okcmd;						\
		}							\
	}								\
	else								\
	{								\
		okcmd;							\
	}								\
}

#define WAITBUSY_2(okcmd, tocmd)					\
	{								\
		register u_short a;					\
		register u_int to = IHFC_NONBUSYTO;			\
									\
		while((~(a = READ_BOTH_2) & 0x100) && --to);		\
									\
		if (!to)						\
		{							\
			NDBGL1(L1_ERROR, "NONBUSY-TIMEOUT! (a=%04x, "	\
			"unit=%d)", a, S_UNIT);				\
			tocmd;						\
		}							\
		else							\
		{							\
			okcmd;						\
		}							\
	}

/*---------------------------------------------------------------------------*
 *	Control function					(HFC-1/S/SP)
 *	
 *	Flag:
 *		1: reset and unlock chip (at boot only)
 *		2: prepare for shutdown  (at shutdown only)
 *		3: reset and resume
 *		4: select TE-mode        (boot default)
 *		5: select NT-mode        (only HFC-S/SP/PCI)
 *
 *	Returns != 0 on errornous chip
 *---------------------------------------------------------------------------*/
int
ihfc_control(ihfc_sc_t *sc, int flag)
{
	BUS_VAR;

	if (flag == 3) goto reset0;
	if (flag == 4)
	{
		S_NTMODE = 0;
		goto mode0;
	}
	if (flag == 5)
	{
		S_NTMODE = 1;
		goto mode0;
	}
	if (flag == 1)
	{
		WRITE_BOTH_2(0x5400 | S_IIO);	/* enable IO (HFC-1/S) */

		S_LAST_CHAN = -1;

		/* HFC-S/SP configuration */

		S_CIRM     = S_IIRQ|0x10; /* IRQ, 8K fifo mode		*/
		S_CLKDEL   = 0x00;	  /* 12.288mhz			*/
		S_CTMT     = 0x03;	  /* transperant mode		*/
		S_CONNECT  = 0x00;	  /* B channel data flow	*/
		S_INT_M1   = 0x40;	  /* interrupt mask		*/
		S_INT_M2   = 0x08;	  /* enable interrupt output	*/
		S_MST_MODE = 0x01; 	  /* master mode		*/
		S_SCTRL    = 0x50;	  /* S/Q on, non cap. line mode */
		S_SCTRL_R  = 0x00;	  /* B channel receive disable	*/
		S_TEST     = 0x00;	  /* no need for awake enable	*/

		if (S_HFC & (HFC_1 | HFC_S))	/* configure timer (50ms) */
		{
			S_CTMT   |= 0x08;
		}
		else
		{
			S_CTMT   |= 0x14;
		}

		/* HFC-1 ISAC configuration (IOM-2 mode) */

		S_ADF1	   = 0x00;	/* */
		S_ADF2	   = 0x80;	/* select mode IOM-2		*/
		S_SPCR	   = 0x00;	/* B channel send disable (0x10 for test loop) */
		S_MASK	   = 0xfb;	/* enable CISQ			*/
		S_MODE	   = 0xc9;	/* receiver enabled		*/
		S_SQXR	   = 0x0f;	/* master, clock always active	*/
		S_STCR	   = 0x70;	/* TIC bus address = 7		*/
		S_STAR2	   = 0x04;	/* enable S/Q			*/

	  mode0:
		if (S_NTMODE)		/* configure NT- or TE-mode */
		{
			S_SCTRL  |=  0x04; 	/* NT mode	*/
			S_CLKDEL &= ~0x7f;	/* clear delay 	*/
			S_CLKDEL |=  0x6c; 	/* set delay	*/
		}
		else
		{
			S_SCTRL  &= ~0x04;	/* TE mode	*/
			S_STDEL  &=  0x7f;	/* use mask!	*/
			S_CLKDEL &= ~0x7f;	/* clear delay	*/
			S_CLKDEL |= S_STDEL;	/* set delay 	*/
		}
		if (S_DLP)		/* configure D-priority */
		{
			S_SCTRL  |= 0x08;	/* (10/11) */
		}
		else
		{
			S_SCTRL  &= ~0x08;	/* (8/9) */
		}

	  reset0:
		/* chip reset (HFC-1/S/SP) */

		if (S_HFC & HFC_1)
		{
			SET_REG((S_CIRM | 0xc8) & 0xdf);

			DELAY(10);	/* HFC-2B manual recommends a 4	*
					 * clock cycle delay after CIRM	*
					 * write with reset=1. A 1us	*
					 * delay, should do for 7.68mhz,*
					 * but just in case I make that	*
					 * 10us.			*/

			SET_REG((S_CIRM | 0xc0) & 0xdf);

			DELAY(250);	/* ISAC manual says reset pulse	*
					 * length is 125us. Accessing	*
					 * ISAC before those 125us, we	*
					 * may risk chip corruption and *
					 * irq failure. The HFC-2B also *
					 * needs some delay to recover, *
					 * so we add some us.		*/
		}
		else
		{
			SET_REG(0x18);

			WRITE_DATA_1(S_CIRM | 8);

			DELAY(10);	/* HFC-2BDS0 manual recommends 	*
					 * a 4 clock cycle delay after	*
					 * CIRM write with reset=1.	*
					 * A 1us delay, should do for	*
					 * 12.288mhz, but just in case	*
					 * I make that 10us.		*/

			WRITE_DATA_1(S_CIRM);

			DELAY(25);	/* HFC-2BDS0 needs some time to	*
					 * recover after CIRM write 	*
					 * with reset=0. Experiments 	*
					 * show this delay should be	*
					 * 8-9us. Just in case we make	*
					 * that 25us.			*/
		}

		{
			/* HFC-1/S/SP chip test				*
			 * 						*
			 * NOTE: after reset the HFC-1/S/SP should be	*
			 * in a mode where it is always non-busy/non-	*
			 * processing, and bit[0] of STATUS/DISBUSY 	*
			 * register, should always return binary '0'	*
			 * until we configure the chips for normal 	*
			 * operation.					*/
#ifdef IHFC_DEBUG
			printf("ihfc: GET_STAT value is: 0x%x\n", GET_STAT);
#endif
			SET_REG(0x30);

			if ((GET_STAT & 1) || (READ_DATA_1 & 0xf)) goto f0;
		}

		ihfc_loadconfig(sc);
		
		if (S_HFC & HFC_1) ihfc_cmdr_hdlr(sc, 0x41);	/* rres, xres */

		S_PHSTATE = 0;
		HFC_FSM(sc, 0);
	}

	if (flag == 2)
	{
		if (S_HFC & HFC_1) S_CIRM &= ~0x03;	/* disable interrupt */

		S_SQXR	   |=  0x40;	/* power down */

		S_INT_M2   &= ~0x01;
		S_MASK	   |=  0x02;

		S_SPCR	   &= ~0x0f;	/* send 1's only */
		S_SCTRL	   &= ~0x83;	/* send 1's only + enable oscillator */

		ihfc_loadconfig(sc);
	}

	return(0);	/* success */

  f0:
	return(1);	/* failure */
}

/*---------------------------------------------------------------------------*
 *	Softc initializer and hardware setup			(HFC-1/S/SP)
 *
 *	Returns: 0 on success
 *		 1 on failure
 *---------------------------------------------------------------------------*/
int
ihfc_init (ihfc_sc_t *sc, u_char chan, int prot, int activate)
{
	if (chan > 5) goto f0;

	chan &= ~1;

	do
	{	if (chan < 2)			/* D-Channel */
		{
			i4b_Dfreembuf(S_MBUF);
			if (!IF_QEMPTY(&S_IFQUEUE)) i4b_Dcleanifq(&S_IFQUEUE);

			RESET_SOFT_CHAN(sc, chan);

			S_IFQUEUE.ifq_maxlen = IFQ_MAXLEN;

#if defined (__FreeBSD__) && __FreeBSD__ > 4
			if(!mtx_initialized(&S_IFQUEUE.ifq_mtx))
				mtx_init(&S_IFQUEUE.ifq_mtx, "i4b_ihfc", NULL, MTX_DEF);
#endif
			if (!activate) continue;

			if (S_HFC & HFC_1)
			{
				S_FILTER  = (chan & 1) ? ihfc_isac_Dread :
							 ihfc_isac_Dwrite;
			}
			else
			{
				S_FILTER  = (chan & 1) ? ihfc_hdlc_Dread :
							 ihfc_hdlc_Dwrite;
			}
		}
		else				/* B-Channel */
		{
			i4b_Bfreembuf(S_MBUF);
			if (!IF_QEMPTY(&S_IFQUEUE))  i4b_Bcleanifq(&S_IFQUEUE);

			RESET_SOFT_CHAN(sc, chan);

			S_IFQUEUE.ifq_maxlen = IFQ_MAXLEN;

#if defined (__FreeBSD__) && __FreeBSD__ > 4
			if(!mtx_initialized(&S_IFQUEUE.ifq_mtx))
				mtx_init(&S_IFQUEUE.ifq_mtx, "i4b_ihfc", NULL, MTX_DEF);
#endif
			S_PROT = prot;

			if (!activate) continue;

			switch(prot)
			{	case(BPROT_NONE):
					S_FILTER = (chan & 1) ? 
						ihfc_trans_Bread : 
						ihfc_trans_Bwrite;
					break;
				case(BPROT_RHDLC):
					S_FILTER = (chan & 1) ? 
						ihfc_hdlc_Bread : 
						ihfc_hdlc_Bwrite;
					break;
				case(5):
					S_FILTER = (chan & 1) ? 
						ihfc_test_Bread : 
						ihfc_test_Bwrite;
					break;
			}
		}
	} while (++chan & 1);

	S_MASK	  |=  0xfb;	/* disable all, but CISQ interrupt (ISAC)	*/
	S_INT_M1  &=  0x40;	/* disable all, but TE/NT state machine (HFC)	*/
	S_SCTRL   &= ~0x03;	/* B1/B2 send disable (HFC)			*/
	S_SPCR	  &= ~0x0f;	/* B1/B2 send disable (ISAC)			*/
	S_SCTRL_R &= ~0x03;	/* B1/B2 receive disable (HFC)			*/

	chan = 0;
	if (S_FILTER)			/* D-Channel active		*/
	{
		S_MASK   &= 0x2e;	/* enable RME, RPF, XPR, EXI	*/
		S_INT_M1 |= 0x24;	/* enable D-receive, D-transmit */
	}

	chan = 2;
	if (S_FILTER)			/* B1-Channel active		*/
	{
		S_SCTRL   |= 1;		/* send enable (HFC)		*/
		S_SPCR	  |= 8;		/* send enable (ISAC)		*/
		S_SCTRL_R |= 1;		/* receive enable (HFC)		*/
		S_INT_M1  |=  0x80;	/* timer enable (HFC)		*/
		S_INT_M1  &= ~0x04;	/* let D-channel use timer too	*/
	}

	chan = 4;
	if (S_FILTER)			/* B2-Channel active		*/
	{
		S_SCTRL   |= 2;		/* send enable (HFC)		*/
		S_SPCR	  |= 2;		/* send enable (ISAC)		*/
		S_SCTRL_R |= 2;		/* receive enable (HFC)		*/
		S_INT_M1  |=  0x80;	/* timer enable (HFC)		*/
		S_INT_M1  &= ~0x04;	/* let D-channel use timer too	*/
	}

	ihfc_loadconfig(sc);

	/* XXX  reset timer? */

	return 0;	/* success */
  f0:
	return 1;	/* failure */
}

/*---------------------------------------------------------------------------*
 *	Load configuration data					(HFC-1/S/SP)
 *---------------------------------------------------------------------------*/
void 
ihfc_loadconfig(ihfc_sc_t *sc)
{
	BUS_VAR;

	if (S_HFC & HFC_1)
	{
		/* HFC-1 chips w/ISAC: */

		const u_char *src = (void *)&S_ISAC_CONFIG;
		const u_char *dst = (void *)&isac_configtable;

		SET_REG((S_CIRM | 0xc0) & 0xdf);

		S_CTMT = (S_CTMT & ~0x14) | ((S_INT_M1 >> 5) & 0x4);

		SET_REG((S_CTMT | 0xe0) & 0xff);

		while(*dst)
		{
			SET_REG(*dst++);	/* set register	*/

			/* write configuration */	
			DISBUSY(WRITE_DATA_1(*src++), break);
		}
	}
	else
	{
		/* HFC-S/SP chips: */

		const u_char *src = (void *)&S_HFC_CONFIG;
		const u_char *dst = (void *)&ihfc_configtable;

		while(*dst)
		{
			SET_REG(*dst++);	/* set register		*/
			WRITE_DATA_1(*src++);	/* write configuration	*/
		}
	}
}

/*---------------------------------------------------------------------------*
 *	Function State Machine handler (PH layer)		(HFC-1/S/SP)
 *
 *	Flag: 0 = Refresh softc S_PHSTATE + take hints
 *	      1 = Activate
 *	      2 = Deactivate
 *
 *	NOTE: HFC-1 only supports TE mode.
 *---------------------------------------------------------------------------*/
void
ihfc_fsm(ihfc_sc_t *sc, int flag)
{
	const struct ihfc_FSMtable *fsmtab;
	u_char ihfc_cmd = 0;
	u_char isac_cmd = 0;
	u_char tmp;
	BUS_VAR;

	/* get current state (rx/downstream) */

	if (S_HFC & HFC_1)
	{
		SET_REG(0x31); DISBUSY(tmp = (READ_DATA_1 >> 2) & 0xf, return);

		fsmtab = (S_NTMODE) ? &ihfc_TEtable2[tmp]:
				      &ihfc_TEtable2[tmp];
	}
	else
	{
		SET_REG(0x30); tmp = READ_DATA_1 & 0xf;

		fsmtab = (S_NTMODE) ? &ihfc_NTtable[tmp]:
				      &ihfc_TEtable[tmp];
	}

	if (fsmtab->string)
	{
		NDBGL1(L1_I_CICO, "%s (ind=0x%x, flag=%d, unit=%d).",
			fsmtab->string, tmp, flag, S_UNIT);
	}
	else
	{
		NDBGL1(L1_I_ERR, "Illegal indicatation (ind=0x%x, "
			"flag=%d, unit=%d).", tmp, flag, S_UNIT);
	}

	/* indication machine / state change					*
	 *									*
	 * Whenever the state of the S0-line changes, we check to see in which	*
	 * direction the change went. Generally upwards means activate, and	*
	 * downwards means deactivate.						*
	 * The test signal is used to ensure proper syncronization.		*/

	if (fsmtab->state == 0)	/* deactivated indication */
	{
		if (S_PHSTATE != 0)
		{
			isac_cmd = 0x3c; /* deactivate DUI */

			i4b_l1_ph_deactivate_ind(S_I4BUNIT);
		}
	}
	if (fsmtab->state == 2)	/* syncronized indication */
	{
		if (S_PHSTATE != 2)
		{
			if (S_NTMODE) ihfc_cmd = 0x80;
		}
	}
	if (fsmtab->state == 3)	/* activated indication */
	{
		if (S_PHSTATE != 3)
		{
			isac_cmd = (S_DLP) ? 0x24  /* activate AR10 */
					   : 0x20; /* activate AR8  */

			i4b_l1_ph_activate_ind(S_I4BUNIT);
		}
	}
	if (fsmtab->state == 4)	/* error indication */
	{
		if (S_PHSTATE < 4)
		{
			isac_cmd = 0x3c; /* deactivate DUI */
		}
	}

	S_PHSTATE = fsmtab->state;

	if ((flag == 1) && (fsmtab->state != 3))
	{
		isac_cmd = (S_DLP) ? 0x24 : 0x20;
		ihfc_cmd = 0x60;
	}
	if ((flag == 2) && (fsmtab->state != 0))
	{
		isac_cmd = 0x3c;
		ihfc_cmd = 0x40;
	}

	/* set new state (tx / upstream)				      *
	 *								      *
	 * NOTE: HFC-S/SP and ISAC transmitters are always active when 	      *
	 * activated state is reached. The bytes sent to the S0-bus are all   *
	 * high impedance, so they do not disturb.			      *
	 * The HFC-1 has a separate SIEMENS S0-device.			      */

	if (S_HFC & HFC_1)
	{
		if (isac_cmd)
		{
			if (S_IOM2) isac_cmd |= 3;

			SET_REG(0x31); DISBUSY(WRITE_DATA_1(isac_cmd), );

			NDBGL1(L1_I_CICO, "(isac_cmd=0x%x, unit=%d).",
				isac_cmd, S_UNIT);
		}
	}
	else
	{
		if (ihfc_cmd || (fsmtab->state == 5))
		{
			SET_REG(0x30); WRITE_DATA_1(ihfc_cmd);

			NDBGL1(L1_I_CICO, "(ihfc_cmd=0x%x, unit=%d).",
				ihfc_cmd, S_UNIT);
		}
	}
}

/*---------------------------------------------------------------------------*
 *	S/Q - channel handler (read)				(HFC-S/SP)
 *---------------------------------------------------------------------------*/
void
ihfc_sq (ihfc_sc_t *sc)
{
	const struct ihfc_SQtable *SQtab;
	register u_char a = 0;
	BUS_VAR;

	if (S_HFC & HFC_1)
	{
		SET_REG(0x31);
		DISBUSY(a = READ_DATA_1, a = 0);

		if (a & 0x80)
		{
			SET_REG(0x3b);
			DISBUSY(a = READ_DATA_1, a = 0);
		}
	}
	else
	{
		SET_REG(0x34);
		a = READ_DATA_1;
	}

	SQtab = (S_NTMODE) ? &ihfc_Qtable[a & 7]:
			     &ihfc_Stable[a & 7];

	if (a & 0x10)
	{
		if (SQtab->string)
		{
			NDBGL1(L1_I_CICO, "%s (unit=%d, int=%x)",
				SQtab->string, S_UNIT, S_INT_S1);
		}
		else
		{
			NDBGL1(L1_ERROR, "Unknown indication = %x (unit=%d)",
				 a & 7, S_UNIT);
		}
	}
}

/*---------------------------------------------------------------------------*
 *	Interrupt handler					(HFC-1)
 *---------------------------------------------------------------------------*/
void
ihfc_intr1 (ihfc_sc_t *sc)
{
	u_char chan;
	u_char tmp;
	BUS_VAR;
	HFC_VAR;

	HFC_BEG;

	SET_REG(0x20); tmp = GET_STAT; DISBUSY(S_ISTA |= READ_DATA_1, );

	if (S_ISTA & 0x04)	/* CIRQ */
	{
		HFC_FSM(sc, 0);

		ihfc_sq(sc);
	}

	S_INTR_ACTIVE = 1;

	if (S_ISTA & 0xc0)	/* RPF or RME */
	{
		chan = 1;
		if (S_FILTER) S_FILTER(sc, chan);
	}
	if (S_ISTA & 0x10)	/* XPR */
	{
		chan = 0;
		if (S_FILTER) S_FILTER(sc, chan);
	}
	if (tmp & 0x04)		/* Timer elapsed (50ms) */
	{
		SET_REG((S_CTMT | 0xf0) & 0xff);

		chan = 6;
		while(chan--)
		{
			if (chan == 1) break;
			if (S_FILTER) S_FILTER(sc, chan);

			HFC_END;
			DELAY(10);
			HFC_BEG;
		}
	}

	S_INTR_ACTIVE = 0;
		
	if (S_ISTA & 0x01)	/* EXIR */
	{
		SET_REG(0x24); DISBUSY(ihfc_exir_hdlr(sc, READ_DATA_1), );
	}

	S_ISTA &= ~(0x1 | 0x4);

	HFC_END;
}

/*---------------------------------------------------------------------------*
 *	Interrupt handler					(HFC-S/SP)
 *---------------------------------------------------------------------------*/
void
ihfc_intr2 (ihfc_sc_t *sc)
{
	u_char chan;
	BUS_VAR;
	HFC_VAR;

	HFC_BEG;

	SET_REG(0x1e); S_INT_S1 = READ_DATA_1;	/* this will enable new interrupts! */

	if (S_INT_S1 & 0x40)
	{
		HFC_FSM(sc, 0); 	/* statemachine changed */

		ihfc_sq(sc);
	}

	S_INTR_ACTIVE = 1;

	if (S_INT_S1 & 0x20)		/* D-Channel frame (rx) */
	{
		chan = 1;
		if (S_FILTER) S_FILTER(sc, chan);
	}
	if (S_INT_S1 & 0x04)		/* D-Channel frame (tx) */
	{
		chan = 0;
		if (S_FILTER && (~S_INT_S1 & 0x80)) S_FILTER(sc, chan);
	}
	if (S_INT_S1 & 0x80)		/* Timer elapsed (50ms) */
	{
		chan = 6;
		while(chan--)
		{
			if (chan == 1) continue;
			if (S_FILTER) S_FILTER(sc, chan);

			HFC_END;
			DELAY(10);
			HFC_BEG;
		}
	}

	S_INTR_ACTIVE = 0;

	HFC_END;
}

/*---------------------------------------------------------------------------*
 *	Select a Bfifo						(HFC-1/S/SP)
 *	and return bytes in FIFO
 *
 *	(this code is optimized)
 *---------------------------------------------------------------------------*/
u_short
ihfc_Bsel_fifo(ihfc_sc_t *sc, u_char chan, u_char flag)
{
	register u_char	 reg = 0x7e + chan;
	register u_short tmp = 0x100;
	register u_short z1;
	register u_short z2;

	BUS_VAR;

	if (S_HFC & (HFC_1 | HFC_S))
	{
		if (S_LAST_CHAN != chan)
		{
			SET_REG(reg);
			DISBUSY(WAITBUSY_2( , return 0), return 0);

			S_LAST_CHAN = chan;
		}
	}
	else
	{
		SET_REG(0x10);
		WRITE_DATA_1(chan - 2);
		DISBUSY( , return 0);
	}

#define FAST_READ (u_char)(tmp = READ_BOTH_2)
#define	FAST_STAT if (tmp & 0x100) DISBUSY( , return 0);

		SET_REG(reg     ); FAST_STAT;	z1  = FAST_READ;
		SET_REG(reg += 4); FAST_STAT; 	z1 |= FAST_READ << 8;
		SET_REG(reg += 4); FAST_STAT;	z2  = FAST_READ;
		SET_REG(reg += 4); FAST_STAT;	z2 |= READ_DATA_1 << 8;

#undef FAST_READ
#undef FAST_STAT

	z1 &= 0x1fff;
	z2 &= 0x1fff;
	
	z1 = 0x5ff - (z2 = z1 - z2 + ((z2 <= z1) ? 0 : 0x600));

	if (chan & 1)
		return(z2);	/* receive channel */
	else
		return(z1);	/* transmit channel */
}

/*---------------------------------------------------------------------------*
 *	Select a Dfifo						(HFC-S/SP)
 *	and return bytes, and frames in FIFO
 *
 *	Flag values:
 *		0x00: select new fifo + update counters
 *		0x10: increment f1 + update counters
 *		0x20: increment f2 + update counters
 *
 *	NOTE: The upper 16bits holds the number of frames in the FIFO.
 *	NOTE: FIFO has to be selected before you can use flags 0x10/0x20.
 *---------------------------------------------------------------------------*/
u_int32_t
ihfc_Dsel_fifo(ihfc_sc_t *sc, u_char chan, u_char flag)
{
	register u_char	 reg = 0x90 + chan;
	register u_short tmp = 0x100;
	register u_char  f1;
	register u_char  f2;
	register u_short z1;
	register u_short z2;

	BUS_VAR;

	if (S_HFC & (HFC_1 | HFC_S))
	{
		switch(flag)
		{
			case(0x10):
			case(0x20):
				SET_REG(reg);
				if (~GET_STAT & 1)
					WAITBUSY_2( , return 0);

				SET_REG(0xa2 - (flag & 0x10) + chan);
				DISBUSY(READ_DATA_1, return 0);

				SET_REG(reg);
				if (~GET_STAT & 1)
					WAITBUSY_2( , return 0);
				break;

			default:
				if (S_LAST_CHAN != chan)
				{
					SET_REG(reg);
					DISBUSY(WAITBUSY_2( , return 0), return 0);

					S_LAST_CHAN = chan;
				}
				break;
		}
	}
	else
	{
		switch(flag)
		{
			case(0x10):
			case(0x20):
				SET_REG(0xb8 - (flag & 0x10) + chan);
				READ_DATA_1;

				DISBUSY( , return 0);

				if (chan & 1)
				{
					/* Before reading a FIFO a change *
					 * FIFO operation must be done.	  *
					 * (see HFC-SP manual p.38)	  */

					SET_REG(0x10);
					WRITE_DATA_1(chan | 4);

					DISBUSY( , return 0);
				}
				break;

			default:
				SET_REG(0x10);
				WRITE_DATA_1(chan | 4);

				DISBUSY( , return 0);
				break;
		}
	}

#define FAST_READ (u_char)(tmp = READ_BOTH_2)
#define	FAST_STAT if (tmp & 0x100) DISBUSY( , return 0);

		if (S_HFC & HFC_SP) reg = 0x80 + chan;

		SET_REG(reg     ); FAST_STAT;	z1  = FAST_READ;
		SET_REG(reg += 4); FAST_STAT; 	z1 |= FAST_READ << 8;
		SET_REG(reg += 4); FAST_STAT;	z2  = FAST_READ;
		SET_REG(reg += 4); FAST_STAT;	z2 |= FAST_READ << 8;

		if (S_HFC & HFC_SP) reg += 0x26;

		SET_REG(reg -= 2); FAST_STAT; f1  = FAST_READ;
		SET_REG(reg += 4); FAST_STAT; f2  = READ_DATA_1;

#undef FAST_READ
#undef FAST_STAT

	if (~chan & 1)
	{					/* XXX was Z1                */
		S_HDLC_DZ_TAB[f1 & 0xf] = z2;	/* We keep track of the 'Z'  *
						 * values for D-channel (tx),*
						 * so we may calculate the # *
						 * of FIFO bytes free when   *
						 * f1 != f2.		     */
		z2 = S_HDLC_DZ_TAB[f2 & 0xf];
	}

	z1 = 0x1ff - (z2 = (z1 - z2) & 0x1ff);
	f1 = 0xf   - (f2 = (f1 - f2) & 0xf);

	if (chan & 1)
		return(z2 | (f2 << 16));	/* receive channel */
	else
		return(z1 | (f1 << 16));	/* transmit channel */
}


/*---------------------------------------------------------------------------*
 *	Data handler for D channel(write) - chan 0		(HFC-S/SP)
 *---------------------------------------------------------------------------*/
static void
ihfc_hdlc_Dwrite (ihfc_sc_t *sc, u_char chan)
{
	register u_int32_t  sendlen;
	register u_short    len;
	register u_char	  * src;

	BUS_VAR;

	if (!S_MBUF && IF_QEMPTY(&S_IFQUEUE)) return;

	sendlen = ihfc_Dsel_fifo(sc, chan, 0);	/* select new fifo	    *
						 * NOTE: the 16 higher bits *
						 * contain the # of frame-  *
						 * etries free in the FIFO  */
	while (sendlen & ~0xffff)
	{
		if (!S_MBUF)
		{
			if (!(S_MBUF = ihfc_getmbuf(sc, chan))) goto j1;
		}

		src = S_MBUFDATA;
		len = S_MBUFLEN;

		if (len >= 0x1ff) goto j0;	/* frame is too big: skip! */

		sendlen &= 0xffff;		/* only keep # of *
						 * bytes free	  */

		SET_REG((S_HFC & HFC_SP) ? 0xac : 0x96);

		while (sendlen--)
		{
			if (!len--) break;

			DISBUSY(WRITE_DATA_1(*src++), sendlen = -1; len++; break);
		}

		if (!++sendlen)			/* out of fifo: suspend */
		{
			S_MBUFDATA = src;
			S_MBUFLEN  = len;
			break;
		}

		sendlen = ihfc_Dsel_fifo(sc, chan, 0x10);	/* inc F1 */
	   j0:
		i4b_Dfreembuf(S_MBUF);
		S_MBUF = NULL;
	}
  j1:
	return;
}

/*---------------------------------------------------------------------------*
 *	Data handler for D channel(read) - chan 1		(HFC-S/SP)
 *
 *	NOTE: Max framelength is (511 - 3) = 508 bytes, when only one frame
 *	      is received at a time.
 *---------------------------------------------------------------------------*/
static void
ihfc_hdlc_Dread (ihfc_sc_t *sc, u_char chan)
{
	register u_char	    tmp = -1;
	register u_char	    to = 15;
	register u_int32_t  reclen;
	register u_short    crc;
	register u_short    len;
	register u_char	  * dst;

	BUS_VAR;

	reclen = ihfc_Dsel_fifo(sc, chan, 0);	/* select new fifo	    *
						 * NOTE: the higher 16 bits *
						 * contain the # of frames  *
						 * to receive.		    */
	while ((reclen & ~0xffff) && to--)
	{
		reclen &= 0xffff;		/* only keep # of	*
						 * bytes to receive	*/

		if (!(S_MBUF = i4b_Dgetmbuf(DCH_MAX_LEN)))
			panic("ihfc_hdlc_Dread: No mbufs(unit=%d)!\n", S_UNIT);

		SET_REG((S_HFC & HFC_SP) ? 0xbd : 0xa7);

		if ((reclen > 2) && (reclen <= (DCH_MAX_LEN+2)))
		{
			dst = S_MBUFDATA;
			len = S_MBUFLEN = (reclen += 1) - 3;
		}
		else
		{
			len = 0;
			dst = NULL;
		}

		crc = -1;	/* NOTE: after a "F1" or "Z1" hardware overflow	*
				 * it appears not to be necessary to reset the 	*
				 * HFC-1/S or SP chips to continue proper	*
				 * operation, only and only, if we always read	*
				 * "Z1-Z2+1" bytes when F1!=F2 followed by a 	*
				 * F2-counter increment. The bi-effect of doing	*
				 * this is the "STAT" field may say frame is ok	*
				 * when the frame is actually bad.		*
				 * The simple solution is to re-CRC the frame	*
				 * including "STAT" field to see if we get	*
				 * CRC == 0x3933. Then we're 99% sure all	*
				 * frames received are good.			*/

		while(reclen--)
		{
			DISBUSY(tmp = READ_DATA_1, break);
			if (len) { len--; *dst++ = tmp; }

			crc = (HDLC_FCS_TAB[(u_char)(tmp ^ crc)] ^ (u_char)(crc >> 8));
		}

		crc ^= 0x3933;

		if (!tmp && !crc)
		{
			ihfc_putmbuf(sc, chan, S_MBUF);
			S_MBUF = NULL;
		}
		else
		{
			NDBGL1(L1_ERROR, "Frame error (len=%d, stat=0x%x, "
				"crc=0x%x, unit=%d)", S_MBUFLEN, (u_char)tmp, crc,
				S_UNIT);

			i4b_Dfreembuf(S_MBUF);
			S_MBUF = NULL;
		}

		reclen = ihfc_Dsel_fifo(sc, chan, 0x20);
	}
}

/*---------------------------------------------------------------------------*
 *	EXIR error handler - ISAC 	(D - channel)		(HFC-1)
 *---------------------------------------------------------------------------*/
void
ihfc_exir_hdlr (ihfc_sc_t *sc, u_char exir)
{
	register u_char a;
	register u_char cmd;

	for (a = 0, cmd = 0; exir; a++, exir >>= 1)
	{
		if (exir & 1)
		{
			NDBGL1(L1_I_ERR, "%s. (unit=%d)",
				ihfc_EXIRtable[a].string, S_UNIT);
			cmd |= ihfc_EXIRtable[a].cmd;
		}
	}

	if (cmd) ihfc_cmdr_hdlr(sc, cmd);
}

/*---------------------------------------------------------------------------*
 *	CMDR handler - ISAC 	(D - channel)			(HFC-1)
 *---------------------------------------------------------------------------*/
void
ihfc_cmdr_hdlr (ihfc_sc_t *sc, u_char cmdr)
{
	BUS_VAR;

	SET_REG(0x21); DISBUSY(WRITE_DATA_1(cmdr); DELAY(30), );
}

/*---------------------------------------------------------------------------*
 *	Data handler for D channel(write) - chan 0 		(HFC-1)
 *---------------------------------------------------------------------------*/
static void
ihfc_isac_Dwrite (ihfc_sc_t *sc, u_char chan)
{
	register u_char   sendlen = 32;
	register u_char   cmd = 0;
	register u_short  len;
	register u_char * src;

	BUS_VAR;

	if (~S_ISTA & 0x10) goto j0;

	if (!S_MBUF)
		if (!(S_MBUF = ihfc_getmbuf(sc, chan))) goto j0;

	len = S_MBUFLEN;
	src = S_MBUFDATA;

	SET_REG(0x00);

	while(sendlen--)	/* write data */
	{
		if (!len--) break;
		DISBUSY(WRITE_DATA_1(*src++), goto a0);
	}

	cmd |= 0x08;
	
	if (!++sendlen)		/* suspend */
	{
		S_MBUFLEN  = len;
		S_MBUFDATA = src;
	}
	else
	{
	   a0:
		i4b_Dfreembuf(S_MBUF);
		S_MBUF = NULL;

		cmd |= 0x02;
	}

	if (cmd) ihfc_cmdr_hdlr(sc, cmd);

	S_ISTA &= ~0x10;
  j0:
	return;
}

/*---------------------------------------------------------------------------*
 *	Data handler for D channel(read) - chan 1 		(HFC-1)
 *---------------------------------------------------------------------------*/
static void
ihfc_isac_Dread (ihfc_sc_t *sc, u_char chan)
{
	register u_char   cmd = 0;
	register u_char   reclen;
	register u_short  tmp;
	register u_short  len;
	register u_char * dst;

	BUS_VAR;

	if (!(S_ISTA & 0xc0)) goto j1;	/* only receive data *
					 * on interrupt	     */

	if (!S_MBUF)
	{
		if (!(S_MBUF = i4b_Dgetmbuf(DCH_MAX_LEN)))
			panic("ihfc%d: (D) Out of mbufs!\n", S_UNIT);
	}

	len = S_MBUFLEN;
	dst = S_MBUFDATA + (DCH_MAX_LEN - len);

	if (S_ISTA & 0x80)	/* RME */
	{
		SET_REG(0x27); DISBUSY(tmp = (READ_DATA_1 ^ 0x20), goto j0);

		if (tmp & 0x70) goto j0;	/* error */

		SET_REG(0x25); DISBUSY(tmp = (READ_DATA_1 & 0x1f), goto j0);

		reclen = (tmp) ? tmp : 32;
	}
	else			/* RPF */
	{
		reclen = 32;
	}

	if ((len -= reclen) <= DCH_MAX_LEN)	/* get data */
	{
		SET_REG(0x00);

		while(reclen--)
		{
			DISBUSY(*dst++ = READ_DATA_1, goto j0);
		}
	}
	else		/* soft rdo or error */
	{
	  j0:	i4b_Dfreembuf(S_MBUF);
		S_MBUF = NULL;
		
		cmd |= 0x40;

		NDBGL1(L1_I_ERR, "Frame error (unit=%d)", S_UNIT);
	}

	if (S_ISTA & 0x80) 	/* frame complete */
	{
		if (S_MBUF)
		{
			S_MBUFLEN = (DCH_MAX_LEN - len);
			ihfc_putmbuf(sc, chan, S_MBUF);
			S_MBUF = NULL;
		}
	}

	if (S_MBUF)	/* suspend */
	{
		S_MBUFLEN = len;
	}

	ihfc_cmdr_hdlr(sc, cmd | 0x80);

	S_ISTA &= ~0xc0;
  j1:
	return;
}

/*---------------------------------------------------------------------------*
 *	Data handler for B channel(write) - chan 2 and 4	(HFC-1/S/SP)
 *
 *	NOTE: No XDU checking!
 *---------------------------------------------------------------------------*/
static void
ihfc_trans_Bwrite (ihfc_sc_t *sc, u_char chan)
{
	register u_short  sendlen;
	register u_short  len;
	register u_char * src;

	BUS_VAR;

	if (!S_MBUF && IF_QEMPTY(&S_IFQUEUE)) return;

	sendlen = (u_short)ihfc_Bsel_fifo(sc, chan, 0);

	SET_REG(0xaa + chan);

	while (1)
	{
		if (!S_MBUF)
		{
			S_MBUF = ihfc_getmbuf(sc, chan);
			if (!S_MBUF) break;
		}

		src = S_MBUFDATA;
		len = S_MBUFLEN;

		while (sendlen--)
		{
			if (!len--) break;

			DISBUSY(WRITE_DATA_1(*src++), sendlen = -1; len++; break);
		}

		if (!++sendlen)		/* out of fifo: Suspend */
		{
			S_MBUFDATA = src;
			S_MBUFLEN  = len;
			break;
		}

		i4b_Dfreembuf(S_MBUF);
		S_MBUF = NULL;
	}
}

/*---------------------------------------------------------------------------*
 *	Data handler for B channel(read) - chan 3 and 5		(HFC-1/S/SP)
 *	(this code is optimized)
 *---------------------------------------------------------------------------*/
static void
ihfc_trans_Bread (ihfc_sc_t *sc, u_char chan)
{
	register u_short  reclen;
	register u_short  tmp;
	register u_short  len;
	register u_char	* dst;

	BUS_VAR;

	reclen = (u_short)ihfc_Bsel_fifo(sc, chan, 0);

	while (1)
	{
		SET_REG(0xba + chan);

		tmp = 0x100;

		if (!S_MBUF)
			if (!(S_MBUF = i4b_Bgetmbuf(BCH_MAX_DATALEN)))
				panic("ihfc%d: (B) Out of mbufs!\n", S_UNIT);

		len   = S_MBUFLEN;
		dst   = S_MBUFDATA + (BCH_MAX_DATALEN - len);

		while (reclen--)
		{
			if (!len--) break;

			if (tmp & 0x100) DISBUSY( , reclen = -1; len++; break);
			*dst++ = (u_char)(tmp = READ_BOTH_2);
		}

		if (~tmp & 0x100)
		{
			SET_REG(0x30);
			READ_DATA_1;	/* a read to the data port	*
					 * will disable the internal	*
					 * disbusy signal for HFC-1/S	*
					 * chips. This is neccessary	*
					 * to avvoid data loss.		*/
		}

		if (!++reclen)		/* out of fifo: suspend */
		{
			S_MBUFLEN = len;
			break;
		}

		S_MBUFLEN = (BCH_MAX_DATALEN - ++len);

		ihfc_putmbuf(sc, chan, S_MBUF);

		S_MBUF = NULL;
	}
}

/*---------------------------------------------------------------------------*
 *	Data handler for B channel(write) - chan 2 and 4	(HFC-1/S/SP)
 *	
 *	NOTE: Software HDLC encoding!
 *---------------------------------------------------------------------------*/
static void
ihfc_hdlc_Bwrite (ihfc_sc_t *sc, u_char chan)
{
	register u_short  blevel  = S_HDLC_BLEVEL;
	register u_char   flag    = S_HDLC_FLAG;
	register u_int    tmp     = S_HDLC_TMP;
	register u_short  crc     = S_HDLC_CRC;
	register u_short  ib      = S_HDLC_IB;
	register u_char * src     = NULL;
	register u_short  len     = 0;
	register u_short  sendlen;
	register u_short  tmp2;

	BUS_VAR;

	if (!S_MBUF && IF_QEMPTY(&S_IFQUEUE) && (flag == 2)) return;

	sendlen = (u_short)ihfc_Bsel_fifo(sc, chan, 0);

	SET_REG(0xaa + chan);

	if (S_MBUF)
	{
		/* resume */

		src = S_MBUFDATA; 
		len = S_MBUFLEN;

		if (sendlen == 0x5ff)
		{ 
			/* XDU */

			flag = -2;
			len  = 0;

			NDBGL1(L1_S_ERR, "XDU (unit=%d)", S_UNIT);
		}
	}

	while (sendlen--)
	{
		HDLC_ENCODE(*src++, len, tmp, tmp2, blevel, ib, crc, flag,
		{/* gfr */
			i4b_Bfreembuf(S_MBUF);
			S_MBUF = ihfc_getmbuf(sc, chan);

			if (S_MBUF)
			{
				src = S_MBUFDATA;
				len = S_MBUFLEN;
			}
			else
			{
				sendlen = 0;	/* Exit after final FS, *
						 * else the buffer will *
						 * only be filled with  *
						 * "0x7e"-bytes!        */
			}
		},
		{/* wrd */
			
			DISBUSY(WRITE_DATA_1((u_char)tmp), sendlen = 0);
		},
		d );
	}

	if (S_MBUF)		/* suspend */
	{
		S_MBUFDATA = src;
		S_MBUFLEN  = len;
	}

	S_HDLC_IB	= ib;
	S_HDLC_BLEVEL	= blevel;
	S_HDLC_TMP  	= tmp;
	S_HDLC_FLAG 	= flag;
	S_HDLC_CRC  	= crc;
}

/*---------------------------------------------------------------------------*
 *	Data handler for B channel(read) - chan 3 and 5		(HFC-1/S/SP)
 *	
 *	NOTE: Software HDLC decoding!
 *---------------------------------------------------------------------------*/
static void
ihfc_hdlc_Bread (ihfc_sc_t *sc, u_char chan)
{
	register u_char   blevel = S_HDLC_BLEVEL;
		 u_char   flag   = S_HDLC_FLAG;
	register u_short  crc    = S_HDLC_CRC;
	register u_int    tmp    = S_HDLC_TMP;
	register u_short  ib     = S_HDLC_IB;
	register u_char * dst    = NULL;
	register u_short  tmp2   = 0x100;
	register u_short  len    = 0;
	register u_short  reclen;

	BUS_VAR;

	if (S_MBUF)
	{
		/* resume */

		len = S_MBUFLEN;
		dst = S_MBUFDATA + (BCH_MAX_DATALEN - len);
	}

	reclen = (u_short)ihfc_Bsel_fifo(sc, chan, 0);

	SET_REG(0xba + chan);

	while (reclen--)
	{
		HDLC_DECODE(*dst++, len, tmp, tmp2, blevel, ib, crc, flag,
		{/* rdd */
			/* if (tmp2 & 0x100) while (GET_STAT & 1);
			 * tmp2 = READ_BOTH_2;
			 */

			DISBUSY(tmp2 = READ_DATA_1, reclen = 0; tmp2 = 0);
		},
		{/* nfr */
			if (!(S_MBUF = i4b_Bgetmbuf(BCH_MAX_DATALEN)))
				panic("ihfc:(B) Out of mbufs!\n");
				
			dst = S_MBUFDATA;
			len = BCH_MAX_DATALEN;
		},
		{/* cfr */
			len = (BCH_MAX_DATALEN - len);

			if ((!len) || (len > BCH_MAX_DATALEN))
			{
				/* NOTE: frames without any data,               *
				 * only crc field, should be silently discared. */

				i4b_Bfreembuf(S_MBUF);
				NDBGL1(L1_S_MSG, "Bad frame (len=%d, unit=%d)", len, S_UNIT);
				goto s0;
			}

			if (crc)
			{	i4b_Bfreembuf(S_MBUF);
				NDBGL1(L1_S_ERR, "CRC (crc=0x%04x, len=%d, unit=%d)", crc, len, S_UNIT);
				goto s0;
			}

			S_MBUFLEN = len;

			ihfc_putmbuf(sc, chan, S_MBUF);
		 s0:
			S_MBUF = NULL;
		},
		{/* rab */
			i4b_Bfreembuf(S_MBUF);
			S_MBUF = NULL;

			NDBGL1(L1_S_MSG, "Read Abort (unit=%d)", S_UNIT);
		},
		{/* rdo */
			i4b_Bfreembuf(S_MBUF);
			S_MBUF = NULL;

			NDBGL1(L1_S_ERR, "RDO (unit=%d)", S_UNIT);
		},
		continue,
		d);
	}

	/* SET_REG(0x30);
	 * if (~tmp2 & 0x100) READ_DATA_1;	kill disbusy signal
	 */

	if (S_MBUF) S_MBUFLEN = len;	/* suspend */

	S_HDLC_IB 	= ib;
	S_HDLC_CRC	= crc;
	S_HDLC_TMP	= tmp;
	S_HDLC_FLAG 	= flag;
	S_HDLC_BLEVEL	= blevel;
}

/*---------------------------------------------------------------------------*
 *	Data handler for B channel(write) - chan 2 and 4	(HFC-1/S/SP)
 *
 *	This filter generates a pattern which is recognized
 *	and examinated and verified by ihfc_test_Bread.
 *
 *	NOTE: This filter is only for testing purpose.
 *---------------------------------------------------------------------------*/
static void
ihfc_test_Bwrite (ihfc_sc_t *sc, u_char chan)
{
	struct mbuf *m;

	register u_char	 fb;
	register u_short sendlen, tlen;
	register u_short xlen = S_HDLC_IB;
	BUS_VAR;

	goto j0;

	while((m = ihfc_getmbuf(sc, chan)))	/* internal loop */
	{
		if (chan == 2)
			ihfc_putmbuf(sc, 5, m);
		else
			ihfc_putmbuf(sc, 3, m);
	}

	j0:

	sendlen = /* (u_short)ihfc_Bsel_fifo(sc, chan, 0); */ 0;

	if (sendlen == 0x5ff) printf("(send empty)");

	SET_REG(0xaa + chan);

	S_BYTES += sendlen;

	tlen    = S_HDLC_CRC;

	if (sendlen > 0x400) printf("(slow: %d)", sendlen);

	fb = 0x80;

	while (sendlen--)
	{
		if (!tlen--) fb |= 0x20;

		if (!xlen--)
		{
			while(GET_STAT & 1);
			WRITE_DATA_1(0x3e);
			xlen = 200;
		}
		else
		{
			while(GET_STAT & 1);
			WRITE_DATA_1((xlen + 1) & 0xef);
		}

		fb = 0;
	}

	S_HDLC_IB = xlen;
}

/*---------------------------------------------------------------------------*
 *	Data handler for B channel(read) - chan 3 and 5		(HFC-1/S/SP)
 *
 *	This filter examins and verifies the pattern
 *	generated by ihfc_test_Bwrite.
 *
 *	NOTE: This filter is only for testing purpose.
 *---------------------------------------------------------------------------*/
static void
ihfc_test_Bread (ihfc_sc_t *sc, u_char chan)
{
	static u_short toterrors = 0;

	register u_short reclen, len, tlen;
	register u_char fb, tmp;

	register u_short xlen = S_HDLC_IB;
	register u_char *dst = NULL;
	register u_char error = S_HDLC_TMP;
	register u_char ecount = S_HDLC_FLAG;

	BUS_VAR;

	if (S_UNIT != 0) return;

	reclen = /* (u_short)ihfc_Bsel_fifo(sc, chan, 0); */ 0;

	S_BYTES += reclen;

	tlen   = S_HDLC_CRC;

	fb = 0x40;

	if (S_MBUF)
	{
		len = S_MBUFLEN;
		dst = S_MBUFDATA + (BCH_MAX_DATALEN - len);
	}
	else
	{
		len = 0;
	}

	SET_REG(0xba + chan);

	while (reclen--)
	{
/*		if (tmp2 & 0x100) while(GET_STAT & 1);
 *		tmp = (u_char)(tmp2 = READ_BOTH_2);
 */
		if (GET_STAT & 1)
		{	
			/* if (!(++busy % 4)) reclen++; */
			while(GET_STAT & 1);
		}

		tmp = READ_DATA_1;

		if ((tmp & 0x3f) == 0x3e)
		{
			if ((BCH_MAX_DATALEN - len) != 201) error |= 4;

			if ((S_MBUF) && (error))
			{
				if (len) { len--; *dst++ = error; }
				if (len) { len--; *dst++ = xlen+1; }
				if (len) { len--; *dst++ = ecount; }

				S_MBUFLEN = BCH_MAX_DATALEN - len;

				if (S_TRACE & TRACE_B_RX)
					ihfc_putmbuf(sc, chan, S_MBUF);
				else
					i4b_Bfreembuf(S_MBUF);

				S_MBUF = NULL;
				printf("(error%d, %d, %d)", S_UNIT, ecount, toterrors++);
			}

			i4b_Bfreembuf(S_MBUF);
			S_MBUF = i4b_Bgetmbuf(BCH_MAX_DATALEN);

			dst = S_MBUFDATA;
			len = BCH_MAX_DATALEN;

			xlen = 200;
			error = 0;
			ecount = 0;

		/*	SET_REG(0xba + chan); */
		}
		else
		{
			if (!xlen) error |= 2;
			if ((tmp ^ xlen--) & 0xef) { error |= 1; ecount++; }
		}
		if (!tlen--) fb |= 0x20;

		if (len--)
		{
			*dst++ = (tmp | fb);
		}
		else
		{
			len++;
		}

		fb = 0;
	}

	if (S_MBUF)
	{
		S_MBUFLEN = len;
	}

	S_HDLC_IB = xlen;
	S_HDLC_TMP = error;
	S_HDLC_FLAG = ecount;
}
