FreeBSD-5.3/sys/dev/idt/idt.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc.
 * 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. All advertising materials mentioning features or use of this software
 *	must display the following acknowledgement:
 *	This product includes software developed by Matriplex, inc.
 * 4. The name of the author may not be used to endorse or promote products
 *	derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 *
 ******************************************************************************
 *
 * This driver is derived from the Nicstar driver by Mark Tinguely, and
 * some of the original driver still exists here.  Those portions are...
 *   Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely
 *   All rights reserved.
 *
 ******************************************************************************
 *
 *  This driver supports the Fore LE155, LE25, and IDT 77211 cards.
 *
 *  ATM CBR connections are supported, and bandwidth is allocated in
 *  slots of 64k each.  Three VBR queues handle traffic for VBR and
 *  UBR.  Two UBR queues prioritize UBR traffic.  ILMI and signalling
 *  get the higher priority queue, as well as UBR traffic that specifies
 *  a peak cell rate.  All other UBR traffic goes into the lower queue.
 *
 ******************************************************************************
 *
 *  The following sysctl variables are used:
 *
 * hw.idt.log_bufstat  (0)   Log free buffers (every few minutes)
 * hw.idt.log_vcs      (0)   Log VC opens, closes, and other events
 * hw.idt.bufs_large  (100)  Max/target number of free 2k buffers
 * hw.idt.bufs_small  (200)  Max/target number of free mbufs
 * hw.idt.cur_large   (R/O)  Current number of free 2k buffers
 * hw.idt.cur_small   (R/O)  Current number of free mbufs
 * hw.idt.qptr_hold    (1)   Optimize TX queue buffer for lowest overhead
 *
 * Note that the read-only buffer counts will not work with multiple cards.
 *
 ******************************************************************************
 *
 *  Assumptions:
 *
 *  1.  All mbuf clusters are 2048 bytes, and aligned.
 *  2.  All mbufs are 256 bytes, and aligned (see idt_intr_tsq).
 *
 *  Bugs:
 *
 *  1.  Function idt_detach() is unusuable because idt_release_mem() is
 *      incomplete.  The mbufs held in the free buffer queues can be
 *      recovered from the "mcheck" hash table.
 *  2.  The memory allocation could be cleaned up quite a bit.
 *
 ******************************************************************************
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/dev/idt/idt.c,v 1.9 2004/01/14 06:14:35 alc Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/sysctl.h>

#include <sys/sockio.h>

#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/mman.h>
#include <machine/clock.h>
#include <machine/cpu.h>	/* bootverbose */

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

#include <net/if.h>
#include <net/if_arp.h>

/* Gross kludge to make lint compile again.  This sucks, but oh well */
#ifdef COMPILING_LINT
#undef MCLBYTES
#undef MCLSHIFT
#define MCLBYTES 2048
#define MCLSHIFT 11
#endif

#if MCLBYTES != 2048
#error "This nicstar driver depends on 2048 byte mbuf clusters."
#endif

#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <netatm/atm_vc.h>

#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_extern.h>

#include <dev/idt/idtreg.h>
#include <dev/idt/idtvar.h>

#define MAXCARDS 10		/* set to impossibly high */

/******************************************************************************
 *
 *  You may change IDT_LBUFS and IDT_SBUFS if you wish.
 */

#define	NICSTAR_LRG_SIZE	2048	/* must be power of two */
#define	IDT_LBUFS		100	/* default number of 2k buffers */
#define	IDT_SBUFS		200	/* default number of 96-byte buffers */

#define	IDT_TST_START		0x1c000	/* transmit schedule table start */
#define	IDT_SCD_START		0x1d000	/* segmentation channel descriptors start */
#define	IDT_SCD_SIZE		509	/* max number of SCD entries */

#define NICSTAR_FIXPAGES        10

static int idt_sysctl_logbufs = 0;	/* periodic buffer status messages */
       int idt_sysctl_logvcs = 0;	/* log VC open & close events      */
static int idt_sysctl_buflarge = IDT_LBUFS;	/* desired large buffer queue */
static int idt_sysctl_bufsmall = IDT_SBUFS;	/* desired small buffer queue */
static int idt_sysctl_curlarge = 0;	/* current large buffer queue */
static int idt_sysctl_cursmall = 0;	/* current small buffer queue */
static int idt_sysctl_qptrhold = 1;	/* hold TX queue pointer back */
       int idt_sysctl_vbriscbr = 0;	/* use CBR slots for VBR VC's */

SYSCTL_NODE(_hw, OID_AUTO, idt, CTLFLAG_RW, 0, "IDT Nicstar");

SYSCTL_INT(_hw_idt, OID_AUTO, log_bufstat, CTLFLAG_RW,
    &idt_sysctl_logbufs, 0, "Log buffer status");
SYSCTL_INT(_hw_idt, OID_AUTO, log_vcs, CTLFLAG_RW,
    &idt_sysctl_logvcs, 0, "Log VC open/close");

SYSCTL_INT(_hw_idt, OID_AUTO, bufs_large, CTLFLAG_RW,
    &idt_sysctl_buflarge, IDT_LBUFS, "Large buffer queue");
SYSCTL_INT(_hw_idt, OID_AUTO, bufs_small, CTLFLAG_RW,
    &idt_sysctl_bufsmall, IDT_SBUFS, "Small buffer queue");
SYSCTL_INT(_hw_idt, OID_AUTO, cur_large, CTLFLAG_RD,
    &idt_sysctl_curlarge, 0, "Current large queue");
SYSCTL_INT(_hw_idt, OID_AUTO, cur_small, CTLFLAG_RD,
    &idt_sysctl_cursmall, 0, "Current small queue");
SYSCTL_INT(_hw_idt, OID_AUTO, qptr_hold, CTLFLAG_RW,
    &idt_sysctl_qptrhold, 1, "Optimize TX queue ptr");
SYSCTL_INT(_hw_idt, OID_AUTO, vbr_is_cbr, CTLFLAG_RW,
    &idt_sysctl_vbriscbr, 0, "Use CBR for VBR VC's");

/******************************************************************************
 *
 * common VCI values
 *
 * 0/0  Idle cells
 * 0/1  Meta signalling
 * x/1  Meta signalling
 * 0/2  Broadcast signalling
 * x/2  Broadcast signalling
 * x/3  Segment OAM F4 flow
 * x/4  End-end OAM F4 flow
 * 0/5  p-p signalling
 * x/5  p-p signalling
 * x/6  rate management
 * 0/14 SPANS
 * 0/15 SPANS
 * 0/16 ILMI
 * 0/18 PNNI
 */

/*******************************************************************************
 *
 *  fixbuf memory map:
 *
 *  0000 - 1fff:  TSQ  Transmit status queue 1024 entries *  8 bytes each
 *  2000 - 3fff:  RSQ  Receive status queue,  512 entries * 16 bytes each
 *  4000 - 5fff:  VBR  segmentation channel queue (highest priority)
 *  6000 - 7fff:  ABR  segmentation channel queue (middle priority)
 *  8000 - 9fff:  UBR  segmentation channel queue (lowest priority)
 *
 *  IDT device memory map:
 *
 *  1fc00:  RX large buffer queue (4k)
 *  1f800:  RX small buffer queue (4k)
 *  1e800:  RX cells FIFO (16k)
 *  1e7f4:  SCD0 - VBR (12)
 *  1e7e8:  SCD1 - ABR (12)
 *  1e7dc:  SCD2 - UBR (12)
 *  1e7db:  CBR SCD end (last word)
 *  1d000:  CBR SCD start (509 entries)
 *  1cfff:  TST end (4095 available slots)
 *  1c000:  TST start (first CBR slot)
 *
 */

static u_long idt_found = 0;

 /* -------- buffer management -------- */
static int nicstar_sram_wr(nicstar_reg_t * const, u_long,
			   int, u_long, u_long, u_long, u_long);
static int nicstar_sram_rd(nicstar_reg_t * const, u_long, u_long *);
static int nicstar_add_buf(nicstar_reg_t * const, struct mbuf *,
			   struct mbuf *, u_long);
static int nicstar_util_rd(nicstar_reg_t * const, u_long, u_long *);
static int nicstar_util_wr(nicstar_reg_t * const, int, u_long, u_long);
      void nicstar_ld_rcv_buf(nicstar_reg_t * const);

 /* -------- interface routines -------- */
int nicstar_output(struct ifnet *, struct mbuf *, struct sockaddr *,
		   struct rtentry *);
void nicstar_start(struct ifnet *);

 /* -------- VCC open/close routines -------- */
static void nicstar_itrx(nicstar_reg_t *);

 /* -------- receiving routines -------- */
static void nicstar_rawc(nicstar_reg_t *);
static void nicstar_recv(nicstar_reg_t *);
static void nicstar_phys(nicstar_reg_t *);

/*******************************************************************************
 *
 *  New functions
 */

static int idt_buffer_init(IDT *);
static struct mbuf *idt_mbufcl_get(void);

static int idt_connect_init(IDT *, int);
static void idt_connect_newvbr(IDT *);

static void idt_intr_tsq(IDT *);

static vm_offset_t idt_malloc_contig(int);

static int idt_mbuf_align(struct mbuf *, struct mbuf *);
static int idt_mbuf_append4(struct mbuf *, char *);
static struct mbuf *idt_mbuf_copy(IDT *, struct mbuf *);
static int idt_mbuf_prepend(struct mbuf *, char *, int);
static int idt_mbuf_used(struct mbuf *);

static int idt_mcheck_add(IDT *, struct mbuf *);
static int idt_mcheck_rem(IDT *, struct mbuf *);
static int idt_mcheck_init(IDT *);

static int idt_queue_flush(CONNECTION *);
static struct mbuf *idt_queue_get(TX_QUEUE *);
static int idt_queue_init(IDT *);
static int idt_queue_put(CONNECTION *, struct mbuf *);

static int idt_receive_aal5(IDT *, struct mbuf *, struct mbuf *);
static void idt_transmit_drop(IDT *, struct mbuf *);
static void idt_transmit_top(IDT *, TX_QUEUE *);

static int idt_slots_add(IDT *, TX_QUEUE *, int);
static int idt_slots_init(IDT *);
static int idt_slots_rem(IDT *, TX_QUEUE *);

static int idt_phys_detect(IDT *);
static void idt_status_bufs(IDT *);
static int idt_status_wait(IDT *);

/******************************************************************************
 *
 *  VBR queue divisor table
 */

static unsigned char vbr_div_m[] = {
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2,
	1, 2, 2, 2, 3, 1, 2, 1, 3, 2, 3, 3, 4, 3, 3, 2, 4, 1, 3, 3,
	1, 5, 5, 4, 4, 5, 4, 4, 6, 5, 1, 5, 4, 6, 2, 6, 7, 7, 4, 1,
	3, 5, 7, 7, 5, 5, 7, 7, 7, 2, 7, 7, 7, 7, 2, 3, 6, 1, 6, 3,
	2, 3, 5, 1, 7, 4, 5, 2, 3, 4, 7, 1, 7, 4, 3, 2, 7, 7, 5, 7,
	1, 7, 5, 7, 5, 2, 7, 3, 4, 6, 7, 1, 1, 7, 4, 7, 5, 7, 2, 5,
	3, 4, 5, 7, 1, 1, 1, 7, 5, 4, 7, 3, 7, 2, 7, 5, 3, 7, 4, 5,
	7, 7, 1, 1, 1, 7, 7, 5, 4, 7, 3, 5, 7, 7, 2, 7, 5, 5, 3, 7,
	4, 5, 6, 7, 7, 1, 1, 1, 1, 7, 7, 7, 5, 5, 4, 7, 3, 3, 5, 5,
	7, 2, 2, 2, 7, 5, 5, 3, 3, 7, 4, 4, 5, 6, 7, 7, 7, 7, 1, 1,
	1, 1, 1, 7, 7, 7, 7, 6, 5, 5, 4, 4, 7, 7, 3, 3, 5, 5, 5, 7,
	7, 2, 2, 2, 2, 7, 7, 5, 5, 5, 3, 3, 3, 7, 7, 4, 4, 5, 5, 5,
	6, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7,
	7, 6, 6, 5, 5, 4, 4, 4, 7, 7, 7, 3, 3, 3, 3, 3, 5, 5, 5, 7,
	7, 7, 7, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 3,
	3, 3, 3, 7, 7, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5,
	5, 4, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3,
	5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5,
	5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
	7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
	6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
	4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};

static unsigned char vbr_div_n[] = {
	127, 127, 127, 127, 127, 127, 127, 127, 125, 111, 100, 91, 83, 77, 71,
	67, 125, 59, 111, 105, 50, 95, 91, 87, 125, 40, 77, 37, 107, 69,
	100, 97, 125, 91, 88, 57, 111, 27, 79, 77, 25, 122, 119, 93, 91,
	111, 87, 85, 125, 102, 20, 98, 77, 113, 37, 109, 125, 123, 69, 17,
	50, 82, 113, 111, 78, 77, 106, 104, 103, 29, 100, 99, 97, 96, 27,
	40, 79, 13, 77, 38, 25, 37, 61, 12, 83, 47, 58, 23, 34, 45,
	78, 11, 76, 43, 32, 21, 73, 72, 51, 71, 10, 69, 49, 68, 48,
	19, 66, 28, 37, 55, 64, 9, 9, 62, 35, 61, 43, 60, 17, 42,
	25, 33, 41, 57, 8, 8, 8, 55, 39, 31, 54, 23, 53, 15, 52,
	37, 22, 51, 29, 36, 50, 50, 7, 7, 7, 48, 48, 34, 27, 47,
	20, 33, 46, 46, 13, 45, 32, 32, 19, 44, 25, 31, 37, 43, 43,
	6, 6, 6, 6, 41, 41, 41, 29, 29, 23, 40, 17, 17, 28, 28,
	39, 11, 11, 11, 38, 27, 27, 16, 16, 37, 21, 21, 26, 31, 36,
	36, 36, 36, 5, 5, 5, 5, 5, 34, 34, 34, 34, 29, 24, 24,
	19, 19, 33, 33, 14, 14, 23, 23, 23, 32, 32, 9, 9, 9, 9,
	31, 31, 22, 22, 22, 13, 13, 13, 30, 30, 17, 17, 21, 21, 21,
	25, 29, 29, 29, 29, 29, 4, 4, 4, 4, 4, 4, 4, 4, 4,
	27, 27, 27, 27, 27, 27, 23, 23, 19, 19, 15, 15, 15, 26, 26,
	26, 11, 11, 11, 11, 11, 18, 18, 18, 25, 25, 25, 25, 7, 7,
	7, 7, 7, 7, 24, 24, 24, 24, 17, 17, 17, 17, 17, 10, 10,
	10, 10, 10, 23, 23, 23, 23, 13, 13, 13, 13, 16, 16, 16, 16,
	19, 19, 22, 22, 22, 22, 22, 22, 22, 22, 22, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 20, 20, 20,
	20, 20, 20, 20, 20, 20, 20, 17, 17, 17, 17, 14, 14, 14, 14,
	14, 11, 11, 11, 11, 11, 11, 19, 19, 19, 19, 19, 8, 8, 8,
	8, 8, 8, 8, 8, 13, 13, 13, 13, 13, 13, 13, 18, 18, 18,
	18, 18, 18, 18, 18, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 17, 17, 17, 17, 17, 17, 17, 17, 17, 12, 12, 12, 12, 12,
	12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 16,
	16, 16, 16, 16, 16, 16, 9, 9, 9, 9, 9, 9, 9, 9, 9,
	11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13,
	15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
	15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13,
	13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
	13, 13, 11, 11, 11, 11, 11, 11, 11, 11, 11, 9, 9, 9, 9,
	9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12,
	12, 12, 12, 12, 12, 12, 12, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 8,
	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	8, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
	11, 11, 11, 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
	3, 3, 3, 3, 3, 3, 3, 3, 3, 10, 10, 10, 10, 10, 10,
	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
	10, 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4,
	4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
	4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9,
	9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
	9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
	5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
	6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8,
	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};

/******************************************************************************
 *
 *  Stop the device (shutdown)
 *
 *  in:  IDT device
 *
 * Date first: 11/14/2000  last: 11/14/2000
 */

void
idt_device_stop(IDT * idt)
{
	u_long val;
	int s;

	s = splimp();

	*(idt->reg_cfg) = 0x80000000;	/* put chip into reset */
	val = *(idt->reg_gp);	/* wait... */
	val |= *(idt->reg_gp);	/* wait... */
	val |= *(idt->reg_gp);	/* wait... */
	*(idt->reg_cfg) = 0;	/* out of reset */

	splx(s);

	return;
}

/******************************************************************************
 *
 *  Initialize the hardware
 */

void
phys_init(nicstar_reg_t * const idt)
{
	int i;
	u_long t;

#ifdef NICSTAR_TESTSRAM
	u_long z, s2, bad;
#endif
	u_long x, s1;
	volatile u_long *regCFG = (volatile u_long *)(idt->virt_baseaddr + REGCFG);
	volatile u_long *regGP = (volatile u_long *)(idt->virt_baseaddr + REGGP);
	volatile u_long stat_val;

	/* clean status bits */
	stat_val = *(volatile u_long *)idt->stat_reg;
	*(volatile u_long *)idt->stat_reg = stat_val | 0xcc30;	/* clear ints */

	idt->flg_le25 = 0;	/* is this FORE LE25 with 77105 PHY? */
	idt->flg_igcrc = 0;	/* ignore receive CRC errors? */
	idt->hardware = "?";

	/* start signalling SAR reset */
	*regCFG = 0x80000000;

	/* SAR reset--clear occurs at lease 2 PCI cycles after setting */
	t = *regGP;		/* wait */
	t = *regCFG;
	*regCFG = 0;		/* clear reset */

	*regGP = 0x00000000;	/* clear PHYS reset */
	*regGP = 0x00000008;	/* start PHYS reset */
	t = *regGP;		/* wait */
	t = *regCFG;
	*regGP = 0x00000001;	/* set while changing SUNI settings */
	t = *regGP;		/* wait */
	t = *regCFG;

	idt->flg_le25 = idt_phys_detect(idt);

	if (idt->flg_le25) {
		idt->cellrate_rmax = 59259;
		idt->cellrate_tmax = 59259;
		idt->cellrate_rcur = 0;
		idt->cellrate_tcur = 0;
		idt->txslots_max = 348;	/* use n*348 for higher resolution */
		idt->txslots_cur = 0;
		nicstar_util_wr(idt, 0, 0x00, 0x00);	/* synch (needed for
							 * 77105?) */
		nicstar_util_wr(idt, 1, 0x00, 0x09);	/* enable interrupts */
		nicstar_util_wr(idt, 1, 0x02, 0x10);	/* 77105 RFLUSH */
		nicstar_util_rd(idt, 0x01, &t);	/* read/clear interrupt flag */
	} else {
		idt->cellrate_rmax = 353207;	/* 2075 slots of 1 DS0 each... */
		idt->cellrate_tmax = 353207;
		idt->cellrate_rcur = 0;
		idt->cellrate_tcur = 0;
		idt->txslots_max = 2075;
		idt->txslots_cur = 0;

		/* initialize the 155Mb SUNI */
		nicstar_util_wr(idt, 0, 0x00, 0x00);	/* sync utopia with SAR */
		nicstar_util_wr(idt, 1, 0x00, 0x00);	/* clear SW reset */
		*regGP = 0x00000000;	/* clear when done with SUNI changes */
	}

#ifdef NICSTAR_TESTSRAM
	/*
	 * this will work with 32K and 128K word RAM  because the pattern
	 * repeats every 4 words
	 */
	for (i = 0; i < 0x20000; i += 4)
		(void)nicstar_sram_wr(idt, i, 4, 0xa5a5a5a5, 0x5a5a5a5a,
		    0xa5a5a5a5, 0x5a5a5a5a);
	for (i = 0; i < 0x20000; i += 2) {
		s1 = nicstar_sram_rd(idt, i, &x);
		s2 = nicstar_sram_rd(idt, i + 1, &z);
		if (s1 || s2 || x != 0xa5a5a5a5 || z != 0x5a5a5a5a) {
			printf("sram fail1 %d 0x%08x 0x%08x\n", i, x, z);
			break;
		}
	}
	for (i = 0; i < 0x20000; i += 4)
		(void)nicstar_sram_wr(idt, i, 4, 0x5a5a5a5a, 0xa5a5a5a5,
		    0x5a5a5a5a, 0xa5a5a5a5);
	for (i = 0; i < 0x20000; i += 2) {
		s1 = nicstar_sram_rd(idt, i, &z);
		s2 = nicstar_sram_rd(idt, i + 1, &x);
		if (s1 || s2 || x != 0xa5a5a5a5 || z != 0x5a5a5a5a) {
			printf("sram fail2 %d 0x%08x 0x%08x\n", i, x, z);
			break;
		}
	}
#endif

	/* flush SRAM */
	for (i = 0; i < 0x20000; i += 4)
		(void)nicstar_sram_wr(idt, i, 4, 0, 0, 0, 0);

	/*
 	 * the memory map for the 32K word card has the
	 * addresses 0x8000, 0x10000, 0x18000 mapped back
	 * to address 0, and 0x8001, ..., 0x18001 is mapped
 	 * to address 1. address 0x4000 is mapped to 0x1c000
 	 */

	/* write in the 0 word, see if we read it at 0x10000 */
	(void)nicstar_sram_wr(idt, 0x0, 1, 0xa5a5a5a5, 0, 0, 0);
	s1 = nicstar_sram_rd(idt, 0x10000, &x);
	(void)nicstar_sram_wr(idt, 0x0, 1, 0, 0, 0, 0);
	if (!s1 && x == 0xa5a5a5a5) {
		device_printf(idt->dev, "32K words of RAM\n");
		idt->sram = 0x4000;
	} else {
		device_printf(idt->dev, "128K words of RAM\n");
		idt->sram = 0x10000;
	}
#ifdef NICSTAR_FORCE32K
	idt->sram = 0x4000;
	device_printf(idt->dev, "forced to 32K words of RAM\n");
#endif

	return;
}

/*  Cellrate notes:
 *       The cellrate for OC3 is 353207.55, rounded down above.  This makes
 *       2075 slots of one DS0 (64003) each.
 *
 *       The ATM25 rate is calculated from 25.6mb divided by 424 bits for
 *       cell plus 8 bits for "opcode" == 432 bits.  59259 * 432 = 25599888.
 *       This provides a 47-byte AAL1 bitrate of 22,281,384 bits/sec, or
 *       348 slots of one DS0 (64027) each.  If 8khz synch events are to
 *       be sent, then only 347 slots are available.
 *
 ******************************************************************************
 *
 *  Physical layer detect
 *
 *  in:  IDT device
 * out:  zero = LE155, NZ = LE25
 *
 * Date first: 10/30/2000  last: 06/08/2001
 */

int
idt_phys_detect(IDT * idt)
{
	u_long t;
	int retval;

	retval = 0;

	nicstar_util_wr(idt, 0, 0x00, 0x00);	/* synch (needed for 77105?) */
	nicstar_util_rd(idt, 0x00, &t);	/* get Master Control Register */

	switch (t) {
	/* 25.6 Mbps ATM PHY with TC & PMD */
	/* http://www.idt.com/products/pages/ATM_Products-77105.html */
	case 0x09:
		device_printf(idt->dev, "ATM card is Fore LE25, PHY=77105\n");
		idt->hardware = "ATM25/77105";
		retval = 1;
		break;

	/* S/UNI-155-LITE */
	/* http://www.pmc-sierra.com/products/details/pm5346/index.html */
	case 0x30:
		device_printf(idt->dev, "ATM card is Fore LE155 or IDT, PHY=PM5346\n");
		idt->hardware = "ATM155/PM5346";
		break;

	/* S/UNI-155-ULTRA */
	/* http://www.pmc-sierra.com/products/details/pm5350/index.html */
	case 0x31:
	case 0x70:
	case 0x78:
		device_printf(idt->dev, "ATM card is Fore LE155, PHY=PM5350\n");
		idt->hardware = "ATM155/PM5350";
		break;

	default:
		device_printf(idt->dev,
			"cannot figure out card type, assuming LE155 (reg=%d).\n",
	    		(int)t);
		idt->hardware = "unknown (LE155?)";
		break;
	}
	return (retval);
}

/*  Register 0 values:
 *       77105  = 0x09
 *       PM5346 = 0x30
 *       PM5250 = 0x31  (actually observed)
 *       PM5350 = 0x70 or 0x78 (according to docs)
 *
 ******************************************************************************
 *
 *  Initialize the data structures
 */

void
nicstar_init(nicstar_reg_t * const idt)
{
	int i;
	vm_offset_t buf;
	u_long *p;

	idt_connect_init(idt, 0);	/* initialize for 0 VPI bits (12 VCI
					 * bits) */

	/* allocate space for TSQ, RSQ, SCD for VBR,ABR, UBR */
	idt->fixbuf = (vm_offset_t)contigmalloc(NICSTAR_FIXPAGES * PAGE_SIZE,
	    M_DEVBUF, M_NOWAIT | M_ZERO, 0x100000, 0xffffffff, 0x2000, 0);
	if (idt->fixbuf == 0)
		return;		/* no space card disabled */

	if (idt_buffer_init(idt))	/* allocate large buffers */
		goto freemem;	/* free memory and return */

	if (idt_mcheck_init(idt))
		goto freemem;

	idt_found++;		/* number of cards found on machine */

	if (bootverbose) {
		printf("nicstar: buffer size %d\n", 0);
	}
	idt_queue_init(idt);	/* initialize all TX_QUEUE structures */
	idt_slots_init(idt);	/* initialize CBR table slots */

	/* initialize variable rate mbuf queues */

	/* TSQ initialization */
	for (p = (u_long *)idt->fixbuf; p < (u_long *)(idt->fixbuf + 0x2000);) {
		*p++ = 0x00000000;
		*p++ = 0x80000000;	/* set empty bit */
	}

	buf = vtophys(idt->fixbuf);
	/* Transmit Status Queue Base */
	*(volatile u_long *)(idt->virt_baseaddr + REGTSQB) = buf;
	/* Transmit Status Queue Head */
	*(volatile u_long *)(idt->virt_baseaddr + REGTSQH) = 0;	/* 8k aligned */
	idt->tsq_base = (u_long *)idt->fixbuf;
	idt->tsq_head = (u_long *)idt->fixbuf;
	idt->tsq_size = 1024;

	/* Recieve Status Queue Base */
	*(volatile u_long *)(idt->virt_baseaddr + REGRSQB) = buf + 0x2000;
	/* Transmit Status Queue Head */
	*(volatile u_long *)(idt->virt_baseaddr + REGRSQH) = 0;	/* 8k aligned */
	idt->rsqh = 0;


	/* Now load receive buffers into SRAM */
	nicstar_ld_rcv_buf(idt);

	/* load variable SCQ */
	(void)nicstar_sram_wr(idt, 0x1e7dc, 4, (u_long)(buf + 0x8000), 0,
	    0xffffffff, 0);	/* SD2 */
	(void)nicstar_sram_wr(idt, 0x1e7e0, 4, 0, 0, 0, 0);
	(void)nicstar_sram_wr(idt, 0x1e7e4, 4, 0, 0, 0, 0);

	(void)nicstar_sram_wr(idt, 0x1e7e8, 4, (u_long)(buf + 0x6000), 0,
	    0xffffffff, 0);	/* SD1 */
	(void)nicstar_sram_wr(idt, 0x1e7ec, 4, 0, 0, 0, 0);
	(void)nicstar_sram_wr(idt, 0x1e7f0, 4, 0, 0, 0, 0);

	(void)nicstar_sram_wr(idt, 0x1e7f4, 4, (u_long)(buf + 0x4000), 0,
	    0xffffffff, 0);	/* SD0 */
	(void)nicstar_sram_wr(idt, 0x1e7f8, 4, 0, 0, 0, 0);
	(void)nicstar_sram_wr(idt, 0x1e7fc, 4, 0, 0, 0, 0);

	/* initialize RCT */
	for (i = 0; i < idt->sram; i += 4) {	/* XXX ifdef table size */
		nicstar_sram_wr(idt, i, 4, 0x0, 0x0, 0x0, 0xffffffff);
	}

	/* VPI/VCI mask is 0 */
	*(volatile u_long *)(idt->virt_baseaddr + REGVMSK) = 0;

	/* Set the Transmit Schedule Table base address */
	*(volatile u_long *)(idt->virt_baseaddr + REGTSTB) = IDT_TST_START;


/* Configuration Register settings:
 * Bit(s)	Meaning					value
 * 31		Software reset				0
 * 30		RESERVED				0
 * 29		Recieve Enabled				1
 * 28-27	Small Buffer Size (host memory)		01 (96 bytes)
 * 26-25	Large Buffer Size (host memory)		00 (2048 bytes)
 * 24		Interrupt on empty free buffer queue	1
 *
 * 23-22	Recieve Status Queue Size (host memory)	10 (8192 bytes)
 * 21		Accpect Invalid cells into Raw Queue	1
 * 20		Ignore General Flow control		1
 *
 * 19-18	VPI/VCI Select				00
 * 17-16	Recieve Connect Table Size		00 (32K SRAM)
 *							10 (128K SRAM)
 *
 * 15		Accpect non-open VPI/VCI to Raw Queue	1
 * 14-12	time to delay after Rx and interrupt	001 (0us)
 *
 * 11		Interrupt when a Raw Cell is added	1
 * 10		Interrupt when Recieve Queue near full	1
 *  9		Recieve RM (PTI = 110 or 111)		1
 *  8		RESERVED				0
 *
 *  7		Interrupt on Timer rollover		1
 *  6		RESERVED				0
 *  5		Transmit Enabled			1
 *  4		Interrupt on Transmit Status Indicator	1
 *
 *  3		Interrupt on transmit underruns		1
 *  2		UTOPIA cell/byte mode			0 (cell)
 *  1		Interrupt on nearly full TSQ		1
 *  0		Enable Physical Interrupt		1
 */

	/* original values:  0x31b09ebb and 0x31b29eb  */
	/*
	 * 11/01/2000: changed from 0x31b09eb to 0x29b09eb for 96-byte
	 * sm-buf
	 */

	if (idt->sram == 0x4000)/* 32K */
		*(volatile u_long *)(idt->virt_baseaddr + REGCFG) = 0x29b09ebb;
	else			/* 128K */
		*(volatile u_long *)(idt->virt_baseaddr + REGCFG) = 0x29b29ebb;

	return;

freemem:
	/* free memory and return */
	idt_release_mem(idt);
	device_printf(idt->dev, "cannot allocate memory\n");
	return;			/* no space card disabled */
}

/******************************************************************************
 *
 *  Release all allocated memory
 *
 *  in:  IDT device
 *
 * Date first: 11/14/2000  last: 11/14/2000
 */

void
idt_release_mem(IDT * idt)
{
	if (idt->fixbuf != 0)
		kmem_free(kernel_map, idt->fixbuf,
			  (NICSTAR_FIXPAGES * PAGE_SIZE));

	if (idt->cbr_base != 0)
		kmem_free(kernel_map, (vm_offset_t)idt->cbr_base, idt->cbr_size);

	printf("%s() is NOT SAFE!\n", __func__);

	/* we also have idt->connection and idt->mcheck to do as well... */
}

/******************************************************************************
 *
 *  Write one to four words to SRAM
 *
 *    writes one to four words into sram starting at "sram_location"
 *
 *    returns -1 if sram location is out of range.
 *    returns count, if count is not in the range from 1-4.
 *    returns 0 if parameters were acceptable
 */

static int
nicstar_sram_wr(nicstar_reg_t * const idt, u_long address, int count,
    u_long data0, u_long data1, u_long data2, u_long data3)
{
	if (address >= 0x20000)	/* bad address */
		return (-1);

	if (idt_status_wait(idt))	/* 12/06/2000 */
		return (-1);

	switch (--count) {
	case 3:
		*(idt->reg_data + 3) = data3;	/* drop down to do others */
	case 2:
		*(idt->reg_data + 2) = data2;	/* drop down to do others */
	case 1:
		*(idt->reg_data + 1) = data1;	/* drop down to do others */
	case 0:
		*idt->reg_data = data0;	/* load last data item */
		break;		/* done loading values */
	default:
		return (count);	/* nothing to do */
	}
						/* write the word(s) */
	*idt->reg_cmd = 0x40000000 | (address << 2) | count;

	return (0);
}

/*  05/31/2001:  Removed wait between data register(s) and write command.
 *       The docs do not state it is helpful, and the example only has one
 *       wait, before the data register load.  The wait time is very high -
 *       aproximately 6 microseconds per wait.
 *
 ******************************************************************************
 *
 *  Read one word from SRAM
 *
 *    reads one word of sram at "sram_location" and places the value
 *    in "answer_pointer"
 *
 *    returns -1 if sram location is out of range.
 *    returns 0 if parameters were acceptable
 */
static int
nicstar_sram_rd(nicstar_reg_t * const idt, u_long address, u_long *data0)
{
	if (address >= 0x20000)	/* bad address */
		return (-1);

	if (idt_status_wait(idt))
		return (-1);

	*idt->reg_cmd = 0x50000000 | (address << 2);	/* read a word */

	if (idt_status_wait(idt))
		return (-1);

	*data0 = *idt->reg_data;/* save word */

	return (0);
}

/*******************************************************************************
 *
 *  Open or Close connection in IDT Receive Connection Table
 *
 *  in:  IDT device, VPI, VCI, opflag (0 = close, 1 = open)
 * out:  zero = success
 *
 *  Date first: 12/14/2000  last: 12/14/2000
 */

int
idt_connect_opencls(IDT * idt, CONNECTION * connection, int opflag)
{
	int address;
	int word1;

	if (connection->vpi >= idt->conn_maxvpi ||
	    connection->vci >= idt->conn_maxvci)
		return (1);

	address = connection->vpi * idt->conn_maxvci + connection->vci;
	address <<= 2;		/* each entry is 4 words */

	if (opflag) {
		switch (connection->aal) {
		case ATM_AAL0:
			word1 = 0x00038000;
			break;	/* raw cell queue */
		case ATM_AAL1:
			word1 = 0x00008000;
			break;	/* Nicstar "AAL0" */
		case ATM_AAL3_4:
			word1 = 0x00018000;
			break;
		case ATM_AAL5:
			word1 = 0x00028000;
			break;
		default:
			return (1);
		}
		nicstar_sram_wr(idt, address, 4, word1, 0, 0, 0xffffffff);
		opflag = 0x00080000;	/* bit-19 set or clear */
	}
	if (idt_status_wait(idt))
		return (1);

	*idt->reg_cmd = 0x20000000 | opflag | address << 2;
	return (0);
}

/*******************************************************************************
 *
 *    nicstar_add_buf    ( card, mbuf1, mbuf2, which_queue)
 *
 *    This adds two buffers to the specified queue. This uses the
 *    mbuf address as handle and the buffer physical address must be
 *    the DMA address.
 *
 *    returns -1 if queue is full, the address is not word aligned, or
 *    an invalid queue is specified.
 *    returns 0 if parameters were acceptable.
 */

int
nicstar_add_buf(nicstar_reg_t * const idt, struct mbuf * buf0,
    struct mbuf * buf1, u_long islrg)
{
	u_long stat_val;
	u_long val0, val1, val2, val3;

	if (islrg > 1)			/* bad buffer size */
		return (-1);

	stat_val = *idt->reg_stat;

	if (islrg) {
		if (stat_val & 0x80)	/* large queue is full */
			return (-1);
	} else if (stat_val & 0x100)	/* small queue is full */
		return (-1);

	if (!buf0 || !buf1 || ((u_long)(buf0->m_data) & 0x7)
	    || ((u_long)(buf1->m_data) & 0x7)) {
		return (-1);		/* buffers must word aligned */
	}
	if (idt->raw_headm == NULL)	/* raw cell buffer pointer not
					 * initialized */
		if (islrg) {
			idt->raw_headm = buf0;
			idt->raw_headp = vtophys(buf0->m_data);
		}
	if (idt_status_wait(idt))	/* 12/06/2000 */
		return (-1);

	val0 = (u_long)buf0;		/* mbuf address is handle */
	val1 = vtophys(buf0->m_data);	/* DMA addr of buff1 */
	val2 = (u_long)buf1;		/* mbuf address is handle */
	val3 = vtophys(buf1->m_data);	/* DMA addr of buff2 */

	*(idt->reg_data + 0) = val0;
	*(idt->reg_data + 1) = val1;
	*(idt->reg_data + 2) = val2;
	*(idt->reg_data + 3) = val3;

	*idt->reg_cmd = 0x60000000 | islrg;

	idt_mcheck_add(idt, buf0);
	idt_mcheck_add(idt, buf1);

	return (0);
}

/******************************************************************************
 *
 *    nicstar_util_rd    ( card, util_location, answer_pointer )
 *
 *    reads one byte from the utility bus at "util_location" and places the
 *    value in "answer_pointer"
 *
 *    returns -1 if util location is out of range.
 *    returns 0 if parameters were acceptable
 */
static int
nicstar_util_rd(nicstar_reg_t * const idt, u_long address, u_long *data)
{

	if (address >= 0x81)			/* bad address */
		return (-1);

	if (idt_status_wait(idt))
		return (-1);

	*idt->reg_cmd = 0x80000200 | address;	/* read a word */

	if (idt_status_wait(idt))
		return (-1);

	*data = *idt->reg_data & 0xff;		/* save word */

	return (0);
}

/******************************************************************************
 *
 *    nicstar_util_wr    ( card, util location, data )
 *
 *    writes one byte to the utility bus at "util_location"
 *
 *    returns -1 if util location is out of range.
 *    returns 0 if parameters were acceptable
 */
static int
nicstar_util_wr(nicstar_reg_t * const idt, int cs, u_long address, u_long data)
{

	if (address >= 0x81)	/* bad address */
		return (-1);
	if (cs > 1)
		return (-1);

	if (idt_status_wait(idt))
		return (-1);

	*idt->reg_data = data & 0xff;	/* load last data item */

	if (cs == 0)
		*idt->reg_cmd = 0x90000100 | address;	/* write the byte, CS1 */
	else
		*idt->reg_cmd = 0x90000200 | address;	/* write the byte, CS2 */

	return (0);
}

/******************************************************************************
 *
 *    nicstar_eeprom_rd    ( card , byte_location )
 *
 *    reads one byte from the utility bus at "byte_location" and return the
 *    value as an integer. this routint is only used to read the MAC address
 *    from the EEPROM at boot time.
 */
int
nicstar_eeprom_rd(nicstar_reg_t * const idt, u_long address)
{
	volatile u_long *regGP = (volatile u_long *)(idt->virt_baseaddr + REGGP);
	volatile u_long gp = *regGP & 0xfffffff0;
	int i, value = 0;

	DELAY(5);		/* make sure idle */
	*regGP = gp | 0x06;	/* CS and Clock high */
	DELAY(5);
	*regGP = gp;		/* CS and Clock low */
	DELAY(5);
	/* toggle in  READ CMD (00000011) */
	*regGP = gp | 0x04;	/* Clock high (data 0) */
	DELAY(5);
	*regGP = gp;		/* CS and Clock low */
	DELAY(5);
	*regGP = gp | 0x04;	/* Clock high (data 0) */
	DELAY(5);
	*regGP = gp;		/* CS and Clock low */
	DELAY(5);
	*regGP = gp | 0x04;	/* Clock high (data 0) */
	DELAY(5);
	*regGP = gp;		/* CS and Clock low */
	DELAY(5);
	*regGP = gp | 0x04;	/* Clock high (data 0) */
	DELAY(5);
	*regGP = gp;		/* CS and Clock low */
	DELAY(5);
	*regGP = gp | 0x04;	/* Clock high (data 0) */
	DELAY(5);
	*regGP = gp;		/* CS and Clock low */
	DELAY(5);
	*regGP = gp | 0x04;	/* Clock high (data 0) */
	DELAY(5);
	*regGP = gp | 0x01;	/* CS and Clock low data 1 */
	DELAY(5);
	*regGP = gp | 0x05;	/* Clock high (data 1) */
	DELAY(5);
	*regGP = gp | 0x01;	/* CS and Clock low data 1 */
	DELAY(5);
	*regGP = gp | 0x05;	/* Clock high (data 1) */
	DELAY(5);
	/* toggle in the address */
	for (i = 7; i >= 0; i--) {
		*regGP = (gp | ((address >> i) & 1));	/* Clock low */
		DELAY(5);
		*regGP = (gp | 0x04 | ((address >> i) & 1));	/* Clock high */
		DELAY(5);
	}
	/* read EEPROM data */
	for (i = 7; i >= 0; i--) {
		*regGP = gp;	/* Clock low */
		DELAY(5);
		value |= ((*regGP & 0x10000) >> (16 - i));
		*regGP = gp | 0x04;	/* Clock high */
		DELAY(5);
	}
	*regGP = gp;		/* CS and Clock low */
	return (value);
}

/*******************************************************************************
 *
 *  Load the card receive buffers
 *
 *  in:  IDT device
 *
 *  Date first: 11/01/2000  last: 05/25/2000
 */

void
nicstar_ld_rcv_buf(IDT * idt)
{
	struct mbuf *m1, *m2;
	u_long stat_reg;
	int card_small;
	int card_large;
	int s;

	s = splimp();

	stat_reg = *(volatile u_long *)idt->stat_reg;

	card_small = (stat_reg & 0xff000000) >> 23;	/* reg is number of
							 * pairs */
	card_large = (stat_reg & 0x00ff0000) >> 15;

	if (idt_sysctl_bufsmall > 510)
		idt_sysctl_bufsmall = 510;
	if (idt_sysctl_buflarge > 510)
		idt_sysctl_buflarge = 510;
	if (idt_sysctl_bufsmall < 10)
		idt_sysctl_bufsmall = 10;
	if (idt_sysctl_buflarge < 10)
		idt_sysctl_buflarge = 10;

	while (card_small < idt_sysctl_bufsmall) {	/* 05/25/2001 from fixed */
		MGETHDR(m1, M_DONTWAIT, MT_DATA);
		if (m1 == NULL)
			break;
		MGETHDR(m2, M_DONTWAIT, MT_DATA);
		if (m2 == NULL) {
			m_free(m1);
			break;
		}
		MH_ALIGN(m1, 96);	/* word align & allow lots of
					 * prepending */
		MH_ALIGN(m2, 96);
		if (nicstar_add_buf(idt, m1, m2, 0)) {
			device_printf(idt->dev,
				"Cannot add small buffers, size=%d.\n",
				card_small);
			m_free(m1);
			m_free(m2);
			break;
		}
		card_small += 2;
	}

	while (card_large < idt_sysctl_buflarge) {	/* 05/25/2001 from fixed */
		m1 = idt_mbufcl_get();
		if (m1 == NULL)
			break;
		m2 = idt_mbufcl_get();
		if (m2 == NULL) {
			m_free(m1);
			break;
		}
		if (nicstar_add_buf(idt, m1, m2, 1)) {
			device_printf(idt->dev,
				"Cannot add large buffers, size=%d.\n",
				card_large);
			m_free(m1);
			m_free(m2);
			break;
		}
		card_large += 2;
	}
	idt_sysctl_curlarge = card_large;
	idt_sysctl_cursmall = card_small;

	splx(s);
}

/*******************************************************************************
 *
 *  Wait for command to finish
 *
 *  in:  IDT device
 * out:  zero = success
 *
 *  Date first: 12/06/2000  last: 12/16/2000
 */

int
idt_status_wait(IDT * idt)
{
	int timeout;

	timeout = 33 * 100;	/* allow 100 microseconds timeout */

	while (*idt->reg_stat & 0x200)
		if (--timeout == 0) {
			device_printf(idt->dev,
				      "timeout waiting for device status.\n");
			idt->stats_cmderrors++;
			return (1);
		}
	return (0);
}

/*******************************************************************************
 *
 *  Log status of system buffers
 *
 *  in:  IDT device
 *
 *  Date first: 10/31/2000  last: 05/25/2001
 */

void
idt_status_bufs(IDT * idt)
{
	u_long stat_reg;
	int card_small;
	int card_large;
	int s;

	s = splimp();

	stat_reg = *(volatile u_long *)idt->stat_reg;

	card_small = (stat_reg & 0xff000000) >> 23;	/* reg is number of
							 * pairs */
	card_large = (stat_reg & 0x00ff0000) >> 15;

	splx(s);

	device_printf(idt->dev, "BUFFER STATUS: small=%d/%d, large=%d/%d.\n",
	    card_small, idt_sysctl_bufsmall,
	    card_large, idt_sysctl_buflarge);
}

/*  Since this is called when the card timer wraps, we should only see
 *  this 16 times (LE155) or 10 (LE25) per hour.
 *
 *******************************************************************************
 *
 *  Add mbuf into "owned" list
 *
 *  in:  IDT device, mbuf
 * out:  zero = success
 *
 * Date first: 11/13/2000  last: 11/13/2000
 */

int
idt_mcheck_add(IDT * idt, struct mbuf * m)
{
	int hpos;
	int s;

	hpos = (((int)m) >> 8) & 1023;
	s = splimp();

	m->m_next = idt->mcheck[hpos];
	idt->mcheck[hpos] = m;

	splx(s);
	return (0);
}

/******************************************************************************
 *
 *  Remove mbuf from "owned" list
 *
 *  in:  IDT device, mbuf
 * out:  zero = success
 *
 * Date first: 11/13/2000  last: 11/13/2000
 */

int
idt_mcheck_rem(IDT * idt, struct mbuf * m)
{
	struct mbuf *nbuf;
	int hpos;
	int s;

	hpos = (((int)m) >> 8) & 1023;
	s = splimp();

	nbuf = idt->mcheck[hpos];

	if (nbuf == m) {
		idt->mcheck[hpos] = m->m_next;
		splx(s);
		m->m_next = NULL;
		return (0);
	}
	while (nbuf != NULL) {
		if (nbuf->m_next != m) {
			nbuf = nbuf->m_next;
			continue;
		}
		nbuf->m_next = m->m_next;
		splx(s);
		m->m_next = NULL;
		return (0);
	}

	splx(s);
	device_printf(idt->dev, "Card should not have this mbuf! %x\n", (int)m);
	return (1);
}

/******************************************************************************
 *
 *  Initialize mbuf "owned" list
 *
 *  in:  IDT device
 * out:  zero = success
 *
 * Date first: 11/13/2000  last: 05/26/2001
 */

int
idt_mcheck_init(IDT * idt)
{
	int size;
	int x;

	size = round_page(sizeof(struct mbuf *) * 1024);
	idt->mcheck = contigmalloc(size, M_DEVBUF, M_NOWAIT,
	    0x100000, 0xffffffff, 0x2000, 0);
	if (idt->mcheck == NULL)
		return (1);

	for (x = 0; x < 1024; x++)
		idt->mcheck[x] = NULL;

	return (0);
}

/******************************************************************************
 *
 *  Allocate contiguous, fixed memory
 *
 *  in:  number of pages
 * out:  pointer, NULL = failure
 *
 * Date first: 11/29/2000  last: 11/29/2000
 */

vm_offset_t
idt_malloc_contig(int pages)
{
	vm_offset_t retval;

	retval = (vm_offset_t)contigmalloc(pages * PAGE_SIZE,
	    M_DEVBUF, M_NOWAIT, 0x100000, 0xffffffff, 0x2000, 0);
#ifdef UNDEF
	printf("idt: vm_offset_t allocated %d pages at %x\n", pages, retval);
#endif

	return (retval);
}

/*******************************************************************************
 *
 *  Initialize all TX_QUEUE structures
 *
 *  in:  IDT device
 * out:  zero = succes
 *
 *  Date first: 11/29/2000  last: 11/29/2000
 */
static int
idt_queue_init(IDT * idt)
{
	TX_QUEUE *txqueue;
	vm_offset_t scqbase;
	int x;

	idt->cbr_size = IDT_MAX_CBRQUEUE * 16 * 64;
	idt->cbr_base = idt_malloc_contig(idt->cbr_size / PAGE_SIZE);
	scqbase = idt->cbr_base;
	if (scqbase == 0)
		return (1);
	idt->cbr_freect = idt->cbr_size / (16 * 64);

	for (x = 0; x < idt->cbr_freect; x++) {
		txqueue = &idt->cbr_txqb[x];
		txqueue->mget = NULL;
		txqueue->mput = NULL;
		txqueue->scd = IDT_SCD_START + x * 12;
		txqueue->scq_base = (u_long *)scqbase;
		txqueue->scq_next = txqueue->scq_base;
		txqueue->scq_last = txqueue->scq_next;
		txqueue->scq_len = 64;	/* all CBR queues use 64 entries */
		txqueue->scq_cur = 0;
		txqueue->rate = 0;
		txqueue->vbr_m = 0;	/* m & n set to zero for CBR */
		txqueue->vbr_n = 0;
		idt->cbr_free[x] = txqueue;
		scqbase += 64 * 16;
		nicstar_sram_wr(idt, txqueue->scd, 4,
		    vtophys(txqueue->scq_base), 0, 0xffffffff, 0);
	}

	txqueue = &idt->queue_vbr;	/* VBR queue */
	txqueue->mget = NULL;
	txqueue->mput = NULL;
	txqueue->scd = 0x1e7f4;
	txqueue->scq_base = (u_long *)(idt->fixbuf + 0x4000);
	txqueue->scq_next = txqueue->scq_base;
	txqueue->scq_last = txqueue->scq_next;
	txqueue->scq_len = 512;	/* all VBR queues use 512 entries */
	txqueue->scq_cur = 0;
	txqueue->rate = 0;
	txqueue->vbr_m = 1;
	txqueue->vbr_n = 1;
	nicstar_sram_wr(idt, txqueue->scd, 4,
	    vtophys(txqueue->scq_base), 0, 0xffffffff, 0);

	txqueue = &idt->queue_abr;	/* ABR queue (not currently used) */
	txqueue->mget = NULL;
	txqueue->mput = NULL;
	txqueue->scd = 0x1e7e8;
	txqueue->scq_base = (u_long *)(idt->fixbuf + 0x6000);
	txqueue->scq_next = txqueue->scq_base;
	txqueue->scq_last = txqueue->scq_next;
	txqueue->scq_len = 512;
	txqueue->scq_cur = 0;
	txqueue->rate = 0;
	txqueue->vbr_m = 1;
	txqueue->vbr_n = 1;
	nicstar_sram_wr(idt, txqueue->scd, 4,
	    vtophys(txqueue->scq_base), 0, 0xffffffff, 0);

	txqueue = &idt->queue_ubr;	/* UBR queue */
	txqueue->mget = NULL;
	txqueue->mput = NULL;
	txqueue->scd = 0x1e7dc;
	txqueue->scq_base = (u_long *)(idt->fixbuf + 0x8000);
	txqueue->scq_next = txqueue->scq_base;
	txqueue->scq_last = txqueue->scq_next;
	txqueue->scq_len = 512;
	txqueue->scq_cur = 0;
	txqueue->rate = 0;
	txqueue->vbr_m = 1;	/* since the ABR queue is lowest priority, */
	txqueue->vbr_n = 1;	/* these factors should never change */
	nicstar_sram_wr(idt, txqueue->scd, 4,
	    vtophys(txqueue->scq_base), 0, 0xffffffff, 0);

	return (0);
}

/*******************************************************************************
 *
 *  Get mbuf chain from TX_QUEUE
 *
 *  in:  CONNECTION
 * out:  mbuf, NULL = empty
 *
 *  Date first: 12/03/2000  last: 12/03/2000
 */
static struct mbuf *
idt_queue_get(TX_QUEUE * txqueue)
{
	struct mbuf *m1, *m2;
	int s;

	if (txqueue == NULL)
		return (NULL);

	s = splimp();

	m1 = txqueue->mget;
	if (m1 != NULL) {
		m2 = m1->m_nextpkt;
		txqueue->mget = m2;
		if (m2 == NULL)	/* is queue empty now? */
			txqueue->mput = NULL;
	}
	splx(s);

	return (m1);
}

/*******************************************************************************
 *
 *  Add mbuf chain to connection TX_QUEUE
 *
 *  in:  CONNECTION, mbuf chain
 * out:  zero = succes
 *
 *  Date first: 12/03/2000  last: 06/01/2001
 */
static int
idt_queue_put(CONNECTION * connection, struct mbuf * m)
{
	TX_QUEUE *txqueue;
	int s;

	if (connection == NULL) {
		m_freem(m);
		return (1);
	}
	txqueue = connection->queue;
	if (txqueue == NULL) {
		m_freem(m);
		return (1);
	}
	m->m_nextpkt = NULL;
	m->m_pkthdr.rcvif = (struct ifnet *) connection;

	s = splimp();

	if (txqueue->mput != NULL) {
		*txqueue->mput = m;
		txqueue->mput = &m->m_nextpkt;
	} else {		/* queue is empty */
		txqueue->mget = m;
		txqueue->mput = &m->m_nextpkt;
	}
	splx(s);

	return (0);
}

/*******************************************************************************
 *
 *  Flush all connection mbufs from TX_QUEUE
 *
 *  in:  CONNECTION
 * out:  zero = succes
 *
 *  Date first: 12/03/2000  last: 12/03/2000
 */
static int
idt_queue_flush(CONNECTION * connection)
{
	TX_QUEUE *txqueue;
	struct mbuf **m0, *m1;
	int s;

	if (connection == NULL)
		return (1);
	txqueue = connection->queue;
	if (txqueue == NULL)
		return (1);

	s = splimp();

	m0 = &txqueue->mget;
	m1 = *m0;
	while (m1 != NULL) {
		if (m1->m_pkthdr.rcvif == (struct ifnet *) connection) {
			*m0 = m1->m_nextpkt;
			m_freem(m1);
			m1 = *m0;
			continue;
		}
		m0 = &m1->m_nextpkt;
		m1 = *m0;
	}
	txqueue->mput = m0;
	splx(s);

	return (0);
}

/*******************************************************************************
 *
 *  Calculate number of table positions for CBR connection
 *
 *  in:  IDT device, PCR (cells/second)
 * out:  table positions needed (minimum = 1)
 *
 *  Date first: 11/29/2000  last: 06/12/2001
 */
int
idt_slots_cbr(IDT * idt, int pcr)
{
	unsigned int bitrate;
	unsigned int slots;
	unsigned int rem;

	if (pcr == 171) {
		if (idt_sysctl_logvcs)
			device_printf(idt->dev,
				"idt_slots_cbr:  CBR channel=64000, 1 slot\n");
		return (1);
	}
	if (pcr < 171) {
		if (idt_sysctl_logvcs)
			device_printf(idt->dev,
				"idt_slots_cbr:  CBR pcr %d rounded up to 1 slot\n", pcr);
		return (1);
	}
	bitrate = pcr * 47 * 8;
	slots = bitrate / 64000;
	rem = bitrate % 64000;
	if (rem && idt_sysctl_logvcs)
		device_printf(idt->dev,
			"idt_slots_cbr: CBR cell rate rounded down to %d from %d\n",
			((slots * 64000) / 376), pcr);	/* slots++;  */

	if (idt_sysctl_logvcs)
		device_printf(idt->dev,
			"idt_slots_cbr:  CBR pcr=%d, slots=%d.\n", pcr, slots);
	return (slots);
}

/*  The original algorithm rounded up or down by 32k, the goal being to
 *  map 64000 requests exactly.  Unfortunately, this caused one particular
 *  SVC to be set one slot too low, causing mbuf cluster starvation.
 *  We can still handle the single 64k channel with a special case, and
 *  let all others fall where they may.
 *
 *******************************************************************************
 *
 *  Add TX QUEUE pointer to slots in CBR table
 *
 *  in:  IDT device, TX_QUEUE, number slots
 * out:  zero = success
 *
 *  Date first: 11/29/2000  last: 06/11/2001
 */
static int
idt_slots_add(IDT * idt, TX_QUEUE * queue, int slots)
{
	TX_QUEUE *curval;
	int p_max;		/* extra precision slots maximum */
	int p_spc;		/* extra precision spacing value */
	int p_ptr;		/* extra precision pointer */
	int qptr, qmax;
	int qlast;
	int scdval;

	if (slots < 1)
		return (1);

	qmax = idt->txslots_max;
	p_max = qmax << 8;
	p_spc = p_max / slots;
	p_ptr = p_spc >> 1;	/* use half spacing for start point */
	qptr = p_ptr >> 8;
	qlast = qptr;

	scdval = 0x20000000 | queue->scd;

	if (CBR_VERBOSE) {
		printf("idt_slots_add: p_max = %d\n", p_max);
		printf("idt_slots_add: p_spc = %d\n", p_spc);
		printf("idt_slots_add: p_ptr = %d\n", p_ptr);
		printf("idt_slots_add: qptr  = %d\n", qptr);
	}
	while (slots) {
		if (qptr >= qmax)	/* handle wrap for empty slot choosing */
			qptr -= qmax;
		curval = idt->cbr_slot[qptr];
		if (curval != NULL) {	/* this slot has CBR, so try next */
			qptr++;	/* next slot */
			continue;
		}
		if (CBR_VERBOSE) {
			printf("idt_slots_add: using qptr %d (%d)\n", qptr, qptr - qlast);
			qlast = qptr;
		}
		idt->cbr_slot[qptr] = queue;
		nicstar_sram_wr(idt, qptr + IDT_TST_START, 1, scdval, 0, 0, 0);
		slots--;
		p_ptr += p_spc;
		if (p_ptr >= p_max)	/* main pointer wrap */
			p_ptr -= p_max;
		qptr = p_ptr >> 8;
	}
	return (0);
}

/* 06/11/2001:  Extra precision pointer is used in order to handle cases where
 *       fractional slot spacing causes a large area of slots to be filled.
 *       This can cause further CBR circuits to get slots that have very
 *       poor spacing.
 *
 *******************************************************************************
 *
 *  Remove TX QUEUE pointer from slots in CBR table
 *
 *  in:  IDT device, TX_QUEUE
 * out:  number of CBR slots released
 *
 *  Date first: 12/03/2000  last: 12/03/2000
 */
static int
idt_slots_rem(IDT * idt, TX_QUEUE * queue)
{
	int qptr, qmax;
	int slots;

	qmax = idt->txslots_max;
	slots = 0;

	for (qptr = 0; qptr < qmax; qptr++) {
		if (idt->cbr_slot[qptr] != queue)
			continue;
		idt->cbr_slot[qptr] = NULL;
		nicstar_sram_wr(idt, qptr + IDT_TST_START, 1, 0x40000000, 0, 0, 0);
		slots++;
	}
	return (slots);
}

/*******************************************************************************
 *
 *  Initialize slots in CBR table
 *
 *  in:  IDT device
 * out:  zero = success
 *
 *  Date first: 11/29/2000  last: 11/29/2000
 */
static int
idt_slots_init(IDT * idt)
{
	int start;		/* table start pointer */
	int qptr;

	start = IDT_TST_START;

	/* first, fill up the TX CBR table with 'VBR' entries  */

	for (qptr = 0; qptr < idt->txslots_max; qptr++) {
		idt->cbr_slot[qptr] = NULL;
		nicstar_sram_wr(idt, qptr + start, 1, 0x40000000, 0, 0, 0);
	}

	/* now write the jump back to the table start */

	nicstar_sram_wr(idt, qptr + start, 1, 0x60000000 | start, 0, 0, 0);

	return (0);
}

/*******************************************************************************
 *
 *  Open output queue for connection
 *
 *  in:  IDT device, connection (class, traf_pcr, & traf_scr fields valid)
 * out:  zero = success
 *
 *  Date first: 11/29/2000  last: 06/13/2001
 */

int
idt_connect_txopen(IDT * idt, CONNECTION * connection)
{
	TX_QUEUE *txqueue;
	int cellrate;
	int cbr_slots;
	int s;

	cellrate = connection->traf_scr;	/* 06/13/2001 use SCR instead
						 * of PCR */

	if (connection->class == T_ATM_UBR) {	/* UBR takes whatever is left
						 * over */
		connection->queue = &idt->queue_ubr;
		if (idt_sysctl_logvcs)
			printf("idt_connect_txopen: UBR connection for %d/%d\n",
			    connection->vpi, connection->vci);
		return (0);
	}
	if (connection->class == T_ATM_ABR) {	/* ABR treated as UBR-plus */
		connection->queue = &idt->queue_abr;
		if (idt_sysctl_logvcs)
			printf("idt_connect_txopen: UBR+ connection for %d/%d\n",
			    connection->vpi, connection->vci);
		return (0);
	}
	if (connection->class == T_ATM_CBR) {
		cbr_slots = idt_slots_cbr(idt, cellrate);
		s = splimp();
		if (cbr_slots > (idt->txslots_max - idt->txslots_cur) ||
		    idt->cbr_freect < 1) {
			splx(s);
			return (1);	/* requested rate not available */
		}
		idt->txslots_cur += cbr_slots;
		idt->cellrate_tcur += cellrate;
		idt->cbr_freect--;
		txqueue = idt->cbr_free[idt->cbr_freect];
		txqueue->rate = cellrate;	/* was connection->traf_pcr */

		if (idt_slots_add(idt, txqueue, cbr_slots)) {
			idt->txslots_cur -= cbr_slots;	/* cannot add CBR slots */
			idt->cellrate_tcur -= cellrate;
			idt->cbr_free[idt->cbr_freect] = txqueue;
			idt->cbr_freect++;
			splx(s);
			return (1);
		}
		splx(s);
		if (idt_sysctl_logvcs)
			printf("idt_connect_txopen: CBR connection for %d/%d\n",
			    connection->vpi, connection->vci);
		connection->queue = txqueue;
	}
	if (connection->class == T_ATM_VBR) {
		txqueue = &idt->queue_vbr;
		connection->queue = txqueue;
		txqueue->rate += connection->traf_scr;	/* from traf_pcr
							 * 12/17/2000 */
		if (idt_sysctl_logvcs)
			printf("idt_connect_txopen: VBR connection for %d/%d\n",
			    connection->vpi, connection->vci);
	}
	idt_connect_newvbr(idt);/* recalculate VBR divisor values */

	if (connection->class == T_ATM_CBR ||
	    connection->class == T_ATM_VBR)
		return (0);

	return (1);		/* unknown class */
}

/*******************************************************************************
 *
 *  Close connection output queue
 *
 *  in:  IDT device, connection (class, traf_pcr, & traf_scr fields valid)
 * out:  zero = success
 *
 *  Date first: 12/03/2000  last: 12/03/2000
 */
int
idt_connect_txclose(IDT * idt, CONNECTION * connection)
{
	TX_QUEUE *txqueue;
	int cellrate;
	int slots;
	int s;

	cellrate = connection->traf_pcr;
	txqueue = connection->queue;
	if (idt_sysctl_logvcs)
		printf("idt_connect_txclose: closing connection for %d/%d\n",
		    connection->vpi, connection->vci);

	idt_queue_flush(connection);	/* flush all connection mbufs */

	if (connection->class == T_ATM_UBR ||	/* UBR takes whatever is left
						 * over */
	    connection->class == T_ATM_ABR) {	/* ABR not supported, use UBR */
		connection->queue = NULL;
		return (0);
	}
	if (connection->class == T_ATM_CBR) {
		slots = idt_slots_rem(idt, txqueue);	/* remove this queue
							 * from CBR slots */
		s = splimp();
		idt->txslots_cur -= slots;
		idt->cellrate_tcur -= cellrate;
		if (txqueue != NULL) {	/* 06/12/2001 check for failure on
					 * open */
			idt->cbr_free[idt->cbr_freect] = txqueue;
			idt->cbr_freect++;
		}
		splx(s);
		connection->queue = NULL;
	}
	if (connection->class == T_ATM_VBR) {
		txqueue = &idt->queue_vbr;
		connection->queue = NULL;
		txqueue->rate -= connection->traf_scr;	/* from traf_pcr
							 * 12/17/2000 */
	}
	idt_connect_newvbr(idt);/* recalculate VBR divisor values */

	if (connection->class == T_ATM_CBR ||
	    connection->class == T_ATM_VBR)
		return (0);

	return (1);		/* unknown class */
}

/*******************************************************************************
 *
 *  Calculate new VBR divisor values
 *
 *  in:  IDT device
 *
 * Date first: 12/03/2000  last: 12/03/2000
 */
static void
idt_connect_newvbr(IDT * idt)
{
	TX_QUEUE *txqueue;
	int rate_newvbr;
	int rate_noncbr;
	int divisor;

	txqueue = &idt->queue_vbr;

	rate_newvbr = txqueue->rate;
	rate_noncbr = idt->cellrate_tmax - idt->cellrate_tcur;

	if (rate_newvbr < 1)	/* keep sane and prevent divide by zero */
		rate_newvbr = 1;

	if (rate_newvbr >= rate_noncbr) {
		txqueue->vbr_m = 1;
		txqueue->vbr_n = 1;
		return;
	}
	divisor = rate_newvbr * 1000;	/* size of lookup table */
	divisor += rate_newvbr >> 1;	/* apply rounding to divide */
	divisor /= rate_noncbr;	/* always < 1000, since newvbr < noncbr */

	if (idt_sysctl_logvcs)
		printf("idt_connect_newvbr: divisor=%d\n", divisor);
	txqueue->vbr_m = vbr_div_m[divisor];
	txqueue->vbr_n = vbr_div_n[divisor];
	if (idt_sysctl_logvcs)
		printf("idt_connect_newvbr: m=%d, n=%d\n", txqueue->vbr_m, txqueue->vbr_n);
}

/*  For VBR, we track the sum of all the VBR peak cellrates, and divide
 *  that from the "remaining" bandwidth, which is total minus current CBR.
 *
 *  We will need to adjust the VBR divisor whenever we add a CBR or VBR.
 *
 *  Because of the integer scalign (1000) preload, the cellrate for the
 *  VBR channel should not exceed 2 million (aprox 5 OC3s).  This is
 *  protected by the check for rate_newvbr >= rate_noncbr.
 *
 *******************************************************************************
 *
 *  Initialize large buffers, indexes, and reference counts
 *
 *  in:  IDT device
 * out:  zero = success
 *
 * Date first: 11/01/2000  last: 05/25/2001
 */

int
idt_buffer_init(IDT * idt)
{

	idt->raw_headm = NULL;	/* nicstar_add_buf() will initialize */
	idt->raw_headp = 0;

	return (0);
}

/*******************************************************************************
 *
 *  Get large buffer from kernel pool
 *
 * out:  mbuf, NULL = error
 *
 * Date first: 05/25/2001  last: 05/25/2001
 */

struct mbuf *
idt_mbufcl_get(void)
{
	struct mbuf *m;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return (NULL);

	MCLGET(m, M_DONTWAIT);
	if (m->m_flags & M_EXT)
		return (m);

	m_freem(m);
	return (NULL);
}

/*******************************************************************************
 *
 *  Initialize connection table
 *
 *  in:  IDT, number of VPI bits (0, 1, or 2)
 * out:  zero = success
 *
 * Date first: 10/29/2000  last: 12/10/2000
 */

int
idt_connect_init(IDT * idt, int vpibits)
{
	CONNECTION *connection;
	int pages;
	int vpi;
	int vci;

	switch (vpibits) {
	case 1:
		idt->conn_maxvpi = 2;
		idt->conn_maxvci = 2048;
		break;
	case 2:
		idt->conn_maxvpi = 4;
		idt->conn_maxvci = 1024;
		break;
	default:
		idt->conn_maxvpi = 1;
		idt->conn_maxvci = 4096;
	}

	pages = (sizeof(CONNECTION) * MAX_CONNECTION) + PAGE_SIZE - 1;
	pages /= PAGE_SIZE;
	idt->connection = contigmalloc(pages * PAGE_SIZE, M_DEVBUF, M_NOWAIT,
	    0x100000, 0xffffffff, 0x2000, 0);
	if (idt->connection == NULL)
		return (1);

	for (vpi = 0; vpi < idt->conn_maxvpi; vpi++)
		for (vci = 0; vci < idt->conn_maxvci; vci++) {
			connection = &idt->connection[vpi * idt->conn_maxvci + vci];
			connection->vccinf = NULL;	/* may want to change to
							 * "unclaimed" */
			connection->status = 0;	/* closed */
			connection->vpi = vpi;
			connection->vci = vci;
			connection->queue = NULL;	/* no current TX queue */
			connection->recv = NULL;	/* no current receive
							 * mbuf */
			connection->rlen = 0;
			connection->maxpdu = 0;
			connection->traf_pcr = 0;
			connection->traf_scr = 0;
			connection->aal = 0;
			connection->class = 0;
			connection->flg_mpeg2ts = 0;
			connection->flg_clp = 0;
		}

	return (0);
}

/*******************************************************************************
 *
 *  Look up a connection
 *
 *  in:  IDT, vpi, vci
 * out:  CONNECTION, NULL=invalid vpi/vci
 *
 * Date first: 10/29/2000  last: 10/29/2000
 */

CONNECTION *
idt_connect_find(IDT * idt, int vpi, int vci)
{
	if (vpi >= idt->conn_maxvpi)
		return (NULL);
	if (vci >= idt->conn_maxvci)
		return (NULL);

	return (&idt->connection[vpi * idt->conn_maxvci + vci]);
}

/******************************************************************************
 *
 *                       MBUF SECTION
 *
 ******************************************************************************
 *
 *  Align data in mbuf (to 32-bit boundary)
 *
 *  in:  mbuf
 * out:  zero = success
 *
 * Date first: 11/08/2000  last: 11/15/2000
 */

int
idt_mbuf_align(struct mbuf * m, struct mbuf * prev)
{
	caddr_t buf_base;
	int buf_size;
	int offset;
	int newlen;
	int count;

	if (m == NULL)
		return (1);
	if (((int)m->m_data & 3) == 0)
		return (0);

	if (m->m_flags & M_EXT) {	/* external storage */
		buf_base = m->m_ext.ext_buf;
		buf_size = m->m_ext.ext_size;

		/*
		 * we should really bail out at this point, since we cannot
		 * just shift the data in an external mbuf
		 */

	} else {
		if (m->m_flags & M_PKTHDR) {	/* internal storage, packet
						 * header */
			buf_base = m->m_pktdat;
			buf_size = MHLEN;
		} else {
			buf_base = m->m_dat;	/* internal storage, no packet
						 * header */
			buf_size = MLEN;
		}
	}
	offset = 4 - ((int)buf_base & 3);
	offset &= 3;
	buf_base += offset;	/* new (aligned) buffer base */

	if (m->m_len + offset > buf_size)	/* not enough space to just
						 * move */
		if (prev != NULL)
			if (idt_mbuf_append4(prev, m->m_data) == 0) {	/* give word to prev
									 * mbuf */
				m->m_data += 4;
				m->m_len -= 4;
			}
	if (m->m_len + offset > buf_size)	/* still not enough space */
		if (m->m_next != NULL) {
			newlen = buf_size - offset;	/* maximum new length */
			newlen &= 0xfffffc;	/* fix the length too... */
			count = buf_size - newlen;	/* bytes we have to get
							 * rid of */
			if (idt_mbuf_prepend(m->m_next, m->m_data + newlen, count) == 0)
				m->m_len = newlen;
		}
	if (m->m_len + offset > buf_size)	/* we're stuck... */
		return (1);

	bcopy(m->m_data, buf_base, m->m_len);	/* move data to aligned
						 * position */
	m->m_data = buf_base;
	return (0);
}

/*******************************************************************************
 *
 *  Append 4 bytes to mbuf
 *
 *  in:  mbuf, data pointer
 * out:  zero = success
 *
 * Date first: 11/08/2000  last: 12/13/2000
 */

int
idt_mbuf_append4(struct mbuf * m, char *newdata)
{
	caddr_t buf_base;
	int buf_size;
	int align;
	int space;

	if (m == NULL)
		return (1);

	if (m->m_flags & M_EXT)	/* external storage */
		return (1);	/* 12/13/2000 we must not touch it */

	if (m->m_flags & M_PKTHDR) {	/* internal storage, packet header */
		buf_base = m->m_pktdat;
		buf_size = MHLEN;
	} else {
		buf_base = m->m_dat;	/* internal storage, no packet header */
		buf_size = MLEN;
	}

	align = (4 - ((int)buf_base & 3)) & 3;
	buf_base += align;
	buf_size -= align;
	buf_size &= 0xfffffc;

	space = buf_size - m->m_len;
	if (space < 4)		/* enough space to add 4 bytes? */
		return (1);

	space -= m->m_data - buf_base;	/* get space at end */

	if (space < 4) {
		bcopy(m->m_data, buf_base, m->m_len);
		m->m_data = buf_base;
	}
	bcopy(newdata, m->m_data + m->m_len, 4);
	m->m_len += 4;

	return (0);
}

/*******************************************************************************
 *
 *  Get current base of data storage
 *
 *  in:  mbuf
 * out:  base
 *
 * Date first: 11/16/2000  last: 11/16/2000
 */

caddr_t
idt_mbuf_base(struct mbuf * m)
{
	if (m == NULL)
		return (NULL);

	if (m->m_flags & M_EXT)	/* external storage */
		return (m->m_ext.ext_buf);

	if (m->m_flags & M_PKTHDR)	/* internal storage, packet header */
		return (m->m_pktdat);

	return (m->m_dat);	/* internal storage, no packet header */
}

/*******************************************************************************
 *
 *  Copy mbuf chain to new chain (aligned)
 *
 *  in:  mbuf
 * out:  new mbuf chain, NULL=error
 *
 * Date first: 11/19/2000  last: 05/25/2001
 */

struct mbuf *
idt_mbuf_copy(IDT * idt, struct mbuf * m)
{
	struct mbuf *nbuf, *dbuf, *sbuf;
	u_char *sptr;
	int slen;
	int clen;

	nbuf = idt_mbufcl_get();
	if (nbuf == NULL)
		return (NULL);
	dbuf = nbuf;
	dbuf->m_len = 0;

	for (sbuf = m; sbuf != NULL; sbuf = sbuf->m_next) {
		sptr = sbuf->m_data;
		slen = sbuf->m_len;
		while (slen) {
			clen = slen;
			if (clen > NICSTAR_LRG_SIZE - dbuf->m_len)
				clen = NICSTAR_LRG_SIZE - dbuf->m_len;
			bcopy(sptr, dbuf->m_data + dbuf->m_len, clen);
			sptr += clen;
			slen -= clen;
			dbuf->m_len += clen;
			if (dbuf->m_len >= NICSTAR_LRG_SIZE) {
				dbuf->m_next = idt_mbufcl_get();
				if (dbuf->m_next == NULL) {
					m_freem(nbuf);
					return (NULL);
				}
				dbuf = dbuf->m_next;
				dbuf->m_len = 0;
			}	/* if need dest buf */
		}		/* while(slen) */
	}			/* for... source buf */
	m_freem(m);
	return (nbuf);
}

/*******************************************************************************
 *
 *  Prepend data to mbuf (no alignment done)
 *
 *  in:  mbuf, data pointer, data length
 * out:  zero = success
 *
 * Date first: 11/15/2000  last: 12/13/2000
 */

int
idt_mbuf_prepend(struct mbuf * m, char *newdata, int newlen)
{
	caddr_t buf_base;
	int buf_size;
	int space;

	if (m == NULL)
		return (1);

	if (m->m_flags & M_EXT)	/* external storage */
		return (1);	/* 12/13/2000 we must not touch it */

	if (m->m_flags & M_PKTHDR) {	/* internal storage, packet header */
		buf_base = m->m_pktdat;
		buf_size = MHLEN;
	} else {
		buf_base = m->m_dat;	/* internal storage, no packet header */
		buf_size = MLEN;
	}

	space = m->m_data - buf_base;

	if (space >= newlen) {	/* already space at head of mbuf */
		m->m_data -= newlen;
		m->m_len += newlen;
		bcopy(newdata, m->m_data, newlen);
		return (0);
	}
	space = buf_size - m->m_len;	/* can we get the space by shifting? */
	if (space < newlen)
		return (1);

	bcopy(m->m_data, m->m_data + newlen, m->m_len);
	bcopy(newdata, m->m_data, newlen);
	m->m_len += newlen;

	return (0);
}

/*******************************************************************************
 *
 *  Get amount of data used in mbuf chain
 *
 *  in:  mbuf chain
 * out:  used space
 *
 * Date first: 11/10/2000  last: 11/10/2000
 */

int
idt_mbuf_used(struct mbuf * mfirst)
{
	struct mbuf *m1;
	int mbuf_used;

	mbuf_used = 0;		/* used mbuf space */

	for (m1 = mfirst; m1 != NULL; m1 = m1->m_next)
		mbuf_used += m1->m_len;

	return (mbuf_used);
}

/*******************************************************************************
 *
 *  Notes on transmit buffers:
 *
 *  According to the IDT Nicstar User Manual (version 1.0 2/26/1997), we must
 *  follow these rules for the transmit buffers (page 66):
 *
 *  1.  The buffer length must not be zero.
 *  2.  The buffer length must be a multiple of four bytes.
 *  3.  The sum of the buffer lengths must be a multiple of 48 bytes if
 *      it is a CS-PDU (eg AAL5).
 *  4.  All buffers for a CS-PDU must be contiguous and grouped (no other
 *      PDU buffers or even TSRs).
 *  5.  For AAL5 PDUs, the buffer lengths must include 8 bytes for the
 *      AAL5 length/control and CRC fields.
 *  6.  For AAL5 PDUs, the buffer length of the last buffer must be > 8 bytes.
 *  7.  For AAL5 PDUs, all buffers containing bytes for the last cell must
 *      have the END_CS_PDU bit set to 1.
 *
 *  Also, from the IDT applications note ("FAQ") 77211_AN_97088.pdf file:
 *    Page 5, under "General Technical Questions" (copied EXACTLY):
 *
 *  5).  Can the NicStar begin segmentation from a non-word aligned buffer?
 *       No, the transmit buffer must point to a word aligned buffer.
 *
 *  Since the buffers MUST be word aligned and MUST be word lengths, we have
 *  two potential problems with M_EXT mbufs:
 *
 *  1.  If the M_EXT mbuf has a non word aligned address, we have to copy
 *      the whole thing to a fresh buffer.  Unless - the previous mbuf is
 *      not M_EXT, and it is short by exactly the same amount.  Unlikely.
 *
 *  2.  If the M_EXT mbuf has a non word length, we have to push those bytes
 *      to the next mbuf.  If the next mbuf is also M_EXT, we are stuck.
 *      Unless - the extra bytes from both mbufs are exactly 4 bytes.  Then
 *      we can MGET an empty buf to splice in between.
 *
 *  Also, these rules mean that if any buffer is not word-length, all of the
 *  following buffers will need to be copied/shifted, unless one or more have
 *  lengths off by the right amount to fix the earlier buffer.
 *
 *******************************************************************************
 *
 *  Put mbuf chain on transmit queue
 *
 *  in:  IDT device, mbuf chain, vpi, vci, flags (2 MPEG2 TS == 8 AAL5 cells)
 * out:  (nothing)
 *
 * Date first: 11/08/2000  last: 05/30/2000
 */

void
idt_transmit(IDT * idt, struct mbuf * mfirst, int vpi, int vci, int flags)
{
	CONNECTION *connection;
	struct mbuf *m1, *m0, *malign, *msend;
	int tot_size, tot_scq, x;
	int this_len;
	int padding;

	connection = idt_connect_find(idt, vpi, vci);
	if (connection == NULL) {	/* this VPI/VCI not open */
		idt_transmit_drop(idt, mfirst);
		return;
	}
	if (connection->queue == NULL) {
		idt_transmit_drop(idt, mfirst);
		connection->vccinf->vc_oerrors++;
		return;
	}
	if (flags)
		connection->flg_mpeg2ts = 1;
	else
		connection->flg_mpeg2ts = 0;

	/*
	 * New strategy:  assume that all the buffers are aligned and word
	 * length.  Drop out and handle exceptions below.
	 */

	tot_size = 0;
	tot_scq = 1;
	malign = NULL;

	for (m1 = mfirst; m1 != NULL; m1 = m1->m_next) {
		this_len = m1->m_len;
		tot_size += this_len;
		tot_scq++;
		if (malign != NULL)
			continue;
		if ((int)(m1->m_data) & 3) {	/* bad alignment */
			malign = m1;
			continue;
		}
		if ((this_len & 3) == 0)	/* mbuf length is ok */
			continue;
		if (m1->m_next != NULL) {	/* bad length (in middle) */
			malign = m1;
			continue;
		}
		padding = 4 - (this_len & 3);
		tot_size += padding;
		m1->m_len += padding;
		break;		/* last mbuf, so avoid the loop test */
	}
	if (malign == NULL) {	/* perfect packet, no copy needed */
		mfirst->m_pkthdr.len = tot_size;
		if (connection->flg_mpeg2ts)
			tot_scq += tot_size / 376;	/* more entries needed
							 * for split */
		mfirst->m_pkthdr.csum_data = tot_scq;

		if (idt_queue_put(connection, mfirst))	/* put packet on TX
							 * queue */
			device_printf(idt->dev, "Cannot queue packet for %d/%d.\n", vpi, vci);
		if (connection->queue->mget == mfirst)	/* was the queue empty? */
			idt_transmit_top(idt, connection->queue);	/* IFF empty, prime it
									 * now */
		return;
	}
	/*
	 * Bad alignment or length, so fall through to old code... The first
	 * alignment problem is at 'malign'
	 */
	if (idt_sysctl_logvcs)
		device_printf(idt->dev, "Bad TX buf alignment, len=%d.\n", tot_size);

	if (idt_mbuf_align(mfirst, NULL)) {
		printf("idt_transmit: cannot align first mbuf.\n");
		idt_transmit_drop(idt, mfirst);
		connection->vccinf->vc_oerrors++;
		return;
	}
	/* find first mbuf with bad alignment (if any) */

	m0 = mfirst;
	for (m1 = mfirst->m_next; m1 != NULL; m0 = m1, m1 = m1->m_next) {
		if (m1->m_len & 3)
			break;
		if ((int)(m1->m_data) & 3)
			break;
	}
	if (m1 != NULL) {
		m1 = idt_mbuf_copy(idt, m1);	/* copy the rest into new
						 * mbufs */
		m0->m_next = m1;
		if (m1 == NULL) {
			printf("idt_transmit: could not copy buffers.\n");
			idt_transmit_drop(idt, mfirst);
			connection->vccinf->vc_oerrors++;
			return;	/* FIX THIS - this path has been taken */
		}
	}
	msend = mfirst;

	/* The mbuf chain is aligned, now we need to pad to word length */

	tot_size = idt_mbuf_used(msend);	/* forget the pkthdr length... */
	msend->m_pkthdr.len = tot_size;

	padding = (4 - (tot_size & 3)) & 3;
	if (padding) {
		for (m1 = msend; m1->m_next != NULL; m1 = m1->m_next);
		m1->m_len += padding;
	}
	x = 1;			/* now calculate the SCQ entries needed */
	for (m1 = msend; m1 != NULL; m1 = m1->m_next)
		x++;
	if (connection->flg_mpeg2ts)
		x += tot_size / 376;	/* more entries needed for split */
	msend->m_pkthdr.csum_data = x;

	/* now we have an mbuf chain, from *msend to *m1 ready to go */

	if (idt_queue_put(connection, msend))	/* put packet on TX queue */
		device_printf(idt->dev, "Cannot queue packet for %d/%d.\n", vpi, vci);

	if (connection->queue->mget == msend)	/* was the queue empty? */
		idt_transmit_top(idt, connection->queue);	/* IFF empty, prime it
								 * now */
}

/*  Notes on mbuf usage in the transmit queue:
 *
 *  m_pkthdr.rcvif       Connection pointer (set by idt_queue_put)
 *  m_pkthdr.len         Length of PDU
 *  m_pkthdr.header      TX queue pointer (06/01/2001)
 *  m_pkthdr.csum_flags  Unused, keep zero
 *  m_pkthdr.csum_data   Number of SCQ entries needed or used
 *
 *******************************************************************************
 *
 *  Drop transmit mbuf chain and update counters
 *
 *  in:  IDT device, mbuf chain
 * out:  (nothing)
 *
 * Date first: 11/08/2000  last: 11/08/2000
 */

void
idt_transmit_drop(IDT * idt, struct mbuf * mfirst)
{
	struct mbuf *next;
	int mesglen;

	mesglen = 0;
	while (mfirst != NULL) {
		mesglen += mfirst->m_len;
		next = m_free(mfirst);
		mfirst = next;
	}
	device_printf(idt->dev, "dropping transmit packet, size=%d\n", mesglen);
	idt->stats_oerrors++;	/* 12/15/2000 */
}

/*******************************************************************************
 *
 *  Put mbuf chain on transmit queue
 *
 *  in:  IDT device, TX_QUEUE
 * out:  (nothing)
 *
 * Date first: 12/03/2000  last: 06/01/2001
 */

void
idt_transmit_top(IDT * idt, TX_QUEUE * txqueue)
{
	CONNECTION *connection;
	struct mbuf *top, *m;
	static int padding[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	int scq_space;
	int val, val1, val3, val4;
	int count, mlen, tlen, pad;
	char *mptr;
	int pdulen;
	int vci, vpi;
	int s;

	if (txqueue == NULL)	/* 12/12/2000 */
		return;
	if (txqueue->mget == NULL)	/* check for empty queue */
		return;

	s = splimp();

	scq_space = txqueue->scq_len - txqueue->scq_cur;

	/* Now we can add the queue entries for the PDUs */

	count = 0;

	for (;;) {
		top = txqueue->mget;	/* next available mbuf */
		if (top == NULL)
			break;

		if (top->m_pkthdr.csum_data + 4 > scq_space)
			break;	/* not enough space for this PDU */

		top = idt_queue_get(txqueue);
		if (top == NULL)
			break;
		connection = (CONNECTION *) top->m_pkthdr.rcvif;
		vpi = connection->vpi;
		vci = connection->vci;
		top->m_pkthdr.header = (void *)connection->queue;

		top->m_pkthdr.csum_data = 0;	/* track actual number of SCQ
						 * entries used */

		tlen = top->m_pkthdr.len;
		switch (connection->aal) {
		case ATM_AAL0:
			val = 0;
			break;
		case ATM_AAL3_4:
			val = 0x04000000;
			break;
		case ATM_AAL5:
			val = 0x08000000;
			break;
		default:
			device_printf(idt->dev, "bad AAL for %d/%d\n", vpi, vci);
			m_freem(top);
			connection->vccinf->vc_oerrors++;
			continue;
		}
		val |= txqueue->vbr_m << 23;
		val |= txqueue->vbr_n << 16;
		val4 = (vpi << 20) | (vci << 4);
		if (connection->flg_clp)
			val4 |= 1;	/* set CLP flag */

		/*
		 * Now we are ready to start mapping the mbuf(s) to transmit
		 * buffer descriptors.  If the MPEG2TS flag is set, we want
		 * to create AAL5 PDUs of exactly 384 data bytes each.
		 */

		pdulen = top->m_pkthdr.len;	/* default case: don't split
						 * PDU */
		pad = 0;
		if (connection->flg_mpeg2ts) {
			if ((pdulen % 376) == 0) {	/* correct multiple */
				pdulen = 376;	/* cut off every pdu at 374
						 * data bytes */
				pad = 8;
			} else
				device_printf(idt->dev, "Bad MPEG2 PDU buffer (%d bytes).\n", pdulen);
		}
		val3 = pdulen;	/* actual (unpadded) PDU length */

		pdulen += (4 - (pdulen & 3)) & 3;

		if (pad == 0) {	/* normal padding (PDU not split) */
			pad = pdulen;
			if (connection->aal == ATM_AAL5)
				pad += 8;
			pad = 48 - (pad % 48);
			if (pad == 48)
				pad = 0;
			if (connection->aal == ATM_AAL5)
				pad += 8;	/* pad of up to 52 is
						 * possible/neccessary */
		}
		tlen = 0;
		for (m = top; m != NULL; m = m->m_next) {
			while ((mlen = m->m_len)) {
				if (mlen + tlen > pdulen)
					mlen = pdulen - tlen;	/* how much of this
								 * buffer can we use? */
				mptr = m->m_data;
				tlen += mlen;	/* length of this PDU */
				m->m_len -= mlen;	/* bytes remaining in
							 * mbuf */
				m->m_data += mlen;	/* new data pointer */

				val1 = val;
				if (tlen > pdulen + pad - 48)	/* is this buffer in the
								 * last cell? */
					val1 |= 0x40000000;	/* last buffer in PDU */

				if (tlen == pdulen) {	/* end of PDU, so figure
							 * padding needed */
					idt->stats_opdus++;	/* 12/15/2000 */
					idt->stats_obytes += pdulen;	/* 12/15/2000 */
					connection->vccinf->vc_opdus++;
					connection->vccinf->vc_obytes += pdulen;
					tlen = 0;
					if (pad <= 8)
						mlen += pad;	/* just "add" padding to
								 * this buffer */
				}
				*txqueue->scq_next++ = val1 | mlen;
				*txqueue->scq_next++ = vtophys(mptr);
				*txqueue->scq_next++ = val3;
				*txqueue->scq_next++ = val4;
				if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4)
					txqueue->scq_next = txqueue->scq_base;
				scq_space--;
				top->m_pkthdr.csum_data++;	/* 12/22/2000 */

				/*
				 * if we need more than 8 bytes of padding,
				 * use the zero-filled buffer defined above.
				 */
				if (tlen == 0 && pad > 8) {	/* end of PDU, do we
								 * need padding? */
					val1 |= 0x40000000;	/* last buffer in PDU */
					*txqueue->scq_next++ = val1 | pad;
					*txqueue->scq_next++ = vtophys(padding);
					*txqueue->scq_next++ = val3;
					*txqueue->scq_next++ = val4;
					if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4)
						txqueue->scq_next = txqueue->scq_base;
					scq_space--;
					top->m_pkthdr.csum_data++;	/* 12/22/2000 */
				}
			}
		}

		/*
		 * Now that we have set up the descriptors, add the entry
		 * for Transmit Status Request so we know when the PDU(s)
		 * are done.
		 */

		*txqueue->scq_next++ = 0xa0000000;	/* TSR with interrupt */
		*txqueue->scq_next++ = (u_long)top;
		*txqueue->scq_next++ = 0;
		*txqueue->scq_next++ = 0;

		if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4)
			txqueue->scq_next = txqueue->scq_base;
		scq_space--;
		top->m_pkthdr.csum_data++;	/* 12/22/2000 */
		count++;

		txqueue->scq_cur += top->m_pkthdr.csum_data;
	}

	/*
	 * 05/31/2001: Optimization: Since writing to SRAM is very
	 * expensive, we will only do this when the pointer is stale (half
	 * of the queue). If the queue is less than 1/4 full, then write the
	 * pointer anyway.
	 */

	if (idt_sysctl_qptrhold) {
		scq_space = txqueue->scq_next - txqueue->scq_last;	/* number pending */
		scq_space /= 4;
		if (scq_space < 0)
			scq_space += txqueue->scq_len;
		if (scq_space * 2 < txqueue->scq_len &&	/* less than half
							 * pending */
		    txqueue->scq_cur > txqueue->scq_len / 4)	/* and queue is active */
			count = 0;
	}
	if (count) {		/* we need to update the queue pointer */
		nicstar_sram_wr(idt, txqueue->scd, 1, vtophys(txqueue->scq_next), 0, 0, 0);
		txqueue->scq_last = txqueue->scq_next;
	}
	splx(s);

	return;
}

/*  Once a packet has been put in the Segmentation Channel Queue, it will
 *  be sent, and then the mbuf will harvested by idt_intr_tsq().  While it
 *  is in the SCQ, m_pkthdr.header is the pointer to the TX queue.  This is
 *  important because if the connection is closed while there are still
 *  mbufs in the SCQ, idt_intr_tsq() still needs to update the TX queue.
 *
 ******************************************************************************
 *
 *  Handle entries in Transmit Status Queue (end of PDU interrupt or TSQ full)
 *
 *  in:  IDT device
 *
 *  Date first: 12/04/2000  last: 06/10/2001
 */
static void
idt_intr_tsq(IDT * idt)
{
	CONNECTION *connection;
	TX_QUEUE *txqueue;
	u_long *tsq_ptr;
	u_long val;
	struct mbuf *m;
	int count, s;

	s = splimp();

	tsq_ptr = idt->tsq_head;

	count = 0;
	while ((tsq_ptr[1] & 0x80000000) == 0) {
		m = (struct mbuf *) tsq_ptr[0];
		if (m != NULL) {/* first test for timer rollover entry */
			if (((int)m & 0x000000ff))	/* now do sanity check
							 * on the mbuf ptr */
				device_printf(idt->dev,
					"DANGER! bad mbuf (%x), stamp=%x\n",
					(int)m, (int)tsq_ptr[1]);
			else {
				connection = (CONNECTION *) m->m_pkthdr.rcvif;
				txqueue = (TX_QUEUE *) m->m_pkthdr.header;
				txqueue->scq_cur -= m->m_pkthdr.csum_data;
				if (txqueue->scq_cur < 0 || txqueue->scq_cur > txqueue->scq_len)
					device_printf(idt->dev, "DANGER! scq_cur is %d\n", txqueue->scq_len);
				m->m_pkthdr.header = NULL;
				m_freem(m);
				idt_transmit_top(idt, txqueue);	/* move more into queue */
			}
		}
		tsq_ptr[0] = 0;
		tsq_ptr[1] = 0x80000000;	/* reset TSQ entry */
		tsq_ptr += 2;
		if (tsq_ptr >= idt->tsq_base + idt->tsq_size * 2)
			tsq_ptr = idt->tsq_base;
		count++;
	}
	idt->tsq_head = tsq_ptr;

	if (count) {
		val = (int)tsq_ptr - (int)idt->tsq_base;
		val -= 8;	/* always stay one behind */
		val &= 0x001ff8;
		*idt->reg_tsqh = val;
	}
	splx(s);
}

/*  There is a problem with the pointer rollover where the SAR will think the
 *  TSQ buffer is full (forever?) unless we hold the head pointer back.
 *  This is not mentioned in the 77211 docs, but is a resolved issue in
 *  revision D of the 77252 chips (see 77252 errata).
 *
 *  If a connection is closed while there are still mbufs in the TX queue,
 *  the connection TX queue pointer will be NULL.  That is why we have a
 *  special copy of the pointer in m_pkthdr.header.  Also, idt_transmit_top()
 *  will allow the TX queue for that connection to empty properly.
 *
 *  It is possible for a TSQ entry to be 0x00ffffff/0x00ffffff, which is
 *  obviously not an mbuf and not a timer rollover entry.  We now have an
 *  mbuf sanity check for this.
 *
 ******************************************************************************
 *
 *    nicstar_itrx ( card )
 *
 *    service error in transmitting PDU interrupt.
 *
*/
static void
nicstar_itrx(nicstar_reg_t * idt)
{
	/* trace mbuf and release */
}

/******************************************************************************
 *
 *  Raw cell receive interrupt
 *
 *    service raw cell reception interrupt.
 *
 */

static void
nicstar_rawc(nicstar_reg_t * idt)
{
	u_long ptr_tail;
	struct mbuf *qmbuf;
	u_long *qptr;
	u_long next_mbuf;
	u_long next_phys;

	if (idt->raw_headm == NULL ||
	    idt->raw_headp == 0) {
		device_printf(idt->dev,
			"RAW cell received, buffers not ready (%x/%x).\n",
			(int)idt->raw_headm, (int)idt->raw_headp);
		return;
	}
	ptr_tail = *(volatile u_long *)(idt->virt_baseaddr + REGRAWT);
	if ((ptr_tail & 0xfffff800) == idt->raw_headp)
		return;		/* still in the same large buffer */

	if ((ptr_tail & 0x7ff) < 64)	/* wait until something in new buffer */
		return;

	qmbuf = idt->raw_headm;
	qptr = (u_long *)qmbuf->m_data;

	next_mbuf = qptr[31 * 16 + 1];	/* next handle (virtual) */
	next_phys = qptr[31 * 16 + 0];	/* next physical address */

	/* if we want to do anything with the raw data, this is the place  */

	idt_mcheck_rem(idt, qmbuf);
	m_free(qmbuf);

	idt->raw_headm = (struct mbuf *) next_mbuf;
	idt->raw_headp = next_phys;
}

/*****************************************************************************
 *
 *  Handle AAL5 PDU length
 *
 *  in:  IDT device, first mbuf in chain, last mbuf
 * out:  zero = success, nz = failure (mbuf chain freed)
 *
 * Date first: 11/18/2000  last: 12/14/2000
 */

int
idt_receive_aal5(IDT * idt, struct mbuf * mfirst, struct mbuf * mdata)
{
	struct mbuf *m2;
	unsigned char *aal5len;
	int plen;
	int diff;

	aal5len = mdata->m_data + mdata->m_len - 6;	/* aal5 length = 16 bits */
	plen = aal5len[0] * 256 + aal5len[1];
	diff = mfirst->m_pkthdr.len - plen;	/* number of bytes to trim */

	if (diff == 0)
		return (0);

	if (diff < 0) {
		device_printf(idt->dev,
			"AAL5 PDU length (%d) greater than cells (%d), discarding\n",
			plen, mfirst->m_pkthdr.len);
		m_freem(mfirst);
		return (1);
	}
	while (mdata->m_len < diff) {	/* last mbuf not big enough */
		diff -= mdata->m_len;
		m2 = mdata;
		m_free(mdata);
		if (mdata == mfirst) {	/* we just tossed the whole PDU */
			device_printf(idt->dev, "AAL5 PDU length failed, discarding.\n");
			return (1);	/* the packetheadr length was bad! */
		}
		for (mdata = mfirst; mdata->m_next != m2; mdata = mdata->m_next);
		mdata->m_next = NULL;	/* remove old link to free'd mbuf */
	}
	mdata->m_len -= diff;	/* trim last mbuf */
	mfirst->m_pkthdr.len = plen;

	return (0);
}

/* 12/14/2000: Removed "pruning" log message.
 *
 *****************************************************************************
 *
 *    nicstar_recv ( card )
 *
 *    rebuilds PDUs from entries in the Recieve Status Queue.
 *
 */
struct rsq_entry {
	u_long vpivci;
	struct mbuf *mdata;
	u_long crc;
	u_long flags;
};

static void
nicstar_recv(nicstar_reg_t * idt)
{
	CONNECTION *connection;
	volatile u_long *regh = (volatile u_long *)(idt->virt_baseaddr + REGRSQH);
	struct rsq_entry *rsq;
	struct mbuf *mdata, *mptr;
	u_long flags;
	u_long crc;
	int vpi;
	int vci;
	int clen;
	int x, s;

	s = splimp();

	rsq = (struct rsq_entry *) (idt->fixbuf + 0x2000 + (idt->rsqh & 0x1ffc));

	if ((rsq->flags & 0x80000000) == 0) {
		splx(s);
		return;
	}
	while (rsq->flags & 0x80000000) {
		vpi = rsq->vpivci >> 16;	/* first, grab the RSQ data */
		vci = rsq->vpivci & 0xffff;
		mdata = rsq->mdata;
		crc = rsq->crc;
		flags = rsq->flags;
		clen = (flags & 0x1ff) * 48;

		rsq->vpivci = 0;/* now recycle the RSQ entry */
		rsq->mdata = NULL;
		rsq->crc = 0;
		rsq->flags = 0;	/* turn off valid bit */
		rsq++;
		if (rsq == (struct rsq_entry *) (idt->fixbuf + 0x4000))
			rsq = (struct rsq_entry *) (idt->fixbuf + 0x2000);

		idt_mcheck_rem(idt, mdata);

		connection = idt_connect_find(idt, vpi, vci);
		if (connection == NULL) {	/* we don't want this PDU */
			printf("nicstar_recv: No connection %d/%d - discarding packet.\n",
			    vpi, vci);
			m_free(mdata);	/* throw mbuf away */
			continue;
		}
		mdata->m_len = clen;

		mptr = connection->recv;
		if (mptr == NULL) {
			if (mdata->m_flags & M_PKTHDR)
				connection->recv = mdata;
			else {
				idt->stats_ierrors++;	/* 12/15/2000 */
				connection->vccinf->vc_ierrors++;
				m_free(mdata);
				continue;
			}
		} else {
			x = 0;
			while (mptr->m_next != NULL) {	/* find last mbuf in
							 * chain */
				mptr = mptr->m_next;
				x++;
				if (x > 25)
					break;
			}
			if (x > 25) {
				mptr = connection->recv;
				printf("nicstar_recv: invalid mbuf chain - probable corruption!\n");
				m_free(mdata);
				idt->stats_ierrors++;	/* 12/15/2000 */
				connection->vccinf->vc_ierrors++;
				connection->recv = NULL;
				connection->rlen = 0;
				continue;
			}
			mptr->m_next = mdata;
		}
		connection->rlen += clen;

		if (flags & 0x2000) {	/* end of PDU */
			mptr = connection->recv;	/* one or more mbufs
							 * will be here */
			clen = connection->rlen;	/* length based on cell
							 * count */
			connection->recv = NULL;
			connection->rlen = 0;

			mptr->m_pkthdr.len = clen;
			mptr->m_pkthdr.rcvif = NULL;
			mptr->m_nextpkt = NULL;

			if (mptr->m_pkthdr.csum_flags) {
				device_printf(idt->dev,
					"received pkthdr.csum_flags=%x\n",
					mptr->m_pkthdr.csum_flags);
				mptr->m_pkthdr.csum_flags = 0;
			}
			if (flags & 0x200 &&	/* bad CRC */
			    idt->flg_igcrc == 0) {
				printf("nicstar_recv: Bad CRC - discarding PDU: %d/%d\n", vpi, vci);
				idt->stats_ierrors++;	/* 12/15/2000 */
				connection->vccinf->vc_ierrors++;
				m_freem(mptr);
				continue;
			}
			if (connection->aal == ATM_AAL5) {
				if (idt_receive_aal5(idt, mptr, mdata))	/* adjust for AAL5
									 * length */
					continue;
			}
			idt->stats_ipdus++;	/* 12/15/2000 */
			idt->stats_ibytes += mptr->m_pkthdr.len;	/* 12/15/2000 */
			connection->vccinf->vc_ipdus++;
			connection->vccinf->vc_ibytes += mptr->m_pkthdr.len;
			idt_receive(idt, mptr, vpi, vci);
		} else if (connection->rlen > connection->maxpdu) {	/* this packet is insane */
			printf("nicstar_recv: Bad packet, len=%d - discarding.\n",
			    connection->rlen);
			connection->recv = NULL;
			connection->rlen = 0;
			idt->stats_ierrors++;	/* 12/15/2000 */
			connection->vccinf->vc_ierrors++;
			m_freem(mptr);
		}		/* end of PDU */
	}

	idt->rsqh = vtophys((u_long)rsq) & 0x1ffc;
	*regh = (idt->rsqh - sizeof(struct rsq_entry)) & 0x1ff0;

	splx(s);
}

/******************************************************************************
 *
 *  Physical Interrupt handler
 *
 *  service phyical interrupt.
 *
 */

static void
nicstar_phys(nicstar_reg_t * idt)
{
	u_long t;

	if (idt->flg_le25) {
		nicstar_util_rd(idt, 0x01, &t);	/* get interrupt cause */
		if (t & 0x01) {
			nicstar_util_wr(idt, 1, 0x02, 0x10);	/* reset rx fifo */
			device_printf(idt->dev, "PHY cleared.\n");
		}
	} else
		device_printf(idt->dev, "Physical interrupt.\n");
}

/******************************************************************************
 *
 *  Status register values
 */

#define STAT_REG_RSQAF   0x0002	/* receive status queue almost full */
#define STAT_REG_LBMT    0x0004	/* large buffer queue empty */
#define STAT_REG_SBMT    0x0008	/* small buffer queue empty */
#define STAT_REG_RAWC    0x0010	/* raw cell interrupt */
#define STAT_REG_EPDU    0x0020	/* end of PDU interrupt */
#define STAT_REG_PHY     0x0400	/* physical interrupt */
#define STAT_REG_TIME    0x0800	/* timer overflow interrupt */
#define STAT_REG_TSQAF   0x1000	/* transmit status queue almost full */
#define STAT_REG_TXIN    0x4000	/* TX PDU incomplete */
#define STAT_REG_TXOK    0x8000	/* TX status indicator */

/******************************************************************************
 *
 *  Interrupt handler
 *
 *    service card interrupt.
 *
 *    nicstar_intr ( card )
 */

void
nicstar_intr(void *arg)
{
	IDT *idt;
	volatile u_long stat_val, config_val;
	int int_flags;
	volatile int i;
	int s;

	idt = (IDT *) arg;

	i = 0;

	s = splnet();

	config_val = *idt->reg_cfg;
	stat_val = *idt->reg_stat;

	int_flags =
	    STAT_REG_TSQAF |	/* transmit status queue almost full */
	    STAT_REG_RSQAF |	/* receive status queue almost full */
	    STAT_REG_RAWC |	/* raw cell interrupt */
	    STAT_REG_EPDU |	/* end of PDU interrupt */
	    STAT_REG_TIME |	/* timer overflow interrupt */
	    STAT_REG_TXIN |	/* TX PDU incomplete */
	    STAT_REG_TXOK;	/* TX status indicator */

	if (idt->flg_le25)
		int_flags |= STAT_REG_PHY;	/* include flag for physical
						 * interrupt */

	if (stat_val & (STAT_REG_LBMT | STAT_REG_SBMT)) {	/* buffer queue(s) empty */
		if (stat_val & STAT_REG_SBMT)
			device_printf(idt->dev, "small free buffer queue empty.\n");
		if (stat_val & STAT_REG_LBMT)
			device_printf(idt->dev, "large free buffer queue empty.\n");
		nicstar_ld_rcv_buf(idt);

		if (*idt->reg_stat & STAT_REG_LBMT) {	/* still empty, so
							 * disable IRQ */
			config_val &= ~0x01000000;
			*idt->reg_cfg = config_val;
		}
	}
	/* loop until no more interrupts to service */

	while (stat_val & int_flags) {
		i++;
		if (i < 0 || i > 100)
			break;

		*idt->reg_stat = stat_val & int_flags;	/* clear status bits */

		if (stat_val & STAT_REG_EPDU) {	/* receive PDU */
			nicstar_recv(idt);
			nicstar_ld_rcv_buf(idt);	/* replace buffers,
							 * moved here 11/14/2000 */
		}
		if (stat_val & STAT_REG_RAWC) {	/* raw cell */
			nicstar_rawc(idt);
		}
		if (stat_val & STAT_REG_TXOK) {	/* transmit complete */
			idt_intr_tsq(idt);
		}
		if (stat_val & STAT_REG_TXIN) {	/* bad transmit */
			nicstar_itrx(idt);
			device_printf(idt->dev, "Bad transmit.\n");
		}
		if (stat_val & STAT_REG_TIME) {	/* timer wrap */
			idt->timer_wrap++;
			idt_intr_tsq(idt);	/* check the TSQ */
			nicstar_recv(idt);	/* check the receive queue */
			if (idt_sysctl_logbufs)
				idt_status_bufs(idt);	/* show the buffer
							 * status */
		}
		if (stat_val & STAT_REG_PHY) {	/* physical interrupt */
			nicstar_phys(idt);
			*idt->reg_stat = STAT_REG_PHY;	/* clear the int flag */
		}
		if (stat_val & STAT_REG_RSQAF) {	/* RSQ almost full */
			nicstar_recv(idt);
			device_printf(idt->dev, "warning, RSQ almost full.\n");
			if (*idt->reg_stat & STAT_REG_RSQAF) {	/* RSQ full */
				printf("RSQ is full, disabling interrupt.\n");
				config_val &= 0x00000800;
				*idt->reg_cfg = config_val;
			}
		}
		if (stat_val & STAT_REG_TSQAF) {	/* TSQ almost full */
			idt_intr_tsq(idt);
			device_printf(idt->dev, "warning, TSQ almost full.\n");
			if (*idt->reg_stat & STAT_REG_TSQAF) {
				printf("TSQ is full, disabling interrupt.\n");
				config_val &= ~0x00000002;
				*idt->reg_cfg = config_val;
			}
		}
		stat_val = *idt->reg_stat;
	}

	splx(s);
	if (i < 1 || i > 50)
		device_printf(idt->dev, "i=%3d, status=%08x\n", i, (int)stat_val);
}