summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authormdodd <mdodd@FreeBSD.org>2002-09-30 05:12:39 +0000
committermdodd <mdodd@FreeBSD.org>2002-09-30 05:12:39 +0000
commiteb286245fd42ae3932bf7b3ed6c7b5aaa4fed3d8 (patch)
treee2a3574008786c5ff9f08b7aaaab6f74a7dd910c /sys/dev
parent13a6ca1e93fbdcf204c248b4e23b94418443dc9e (diff)
downloadFreeBSD-src-eb286245fd42ae3932bf7b3ed6c7b5aaa4fed3d8.zip
FreeBSD-src-eb286245fd42ae3932bf7b3ed6c7b5aaa4fed3d8.tar.gz
HARP driver for the IDT77201/211 NICStAR ATM Adapter (Including Fore LE155).
Obtained from: Richard Hodges <rh@matriplex.com>
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/idt/idt.c3307
-rw-r--r--sys/dev/idt/idt_harp.c764
-rw-r--r--sys/dev/idt/idt_pci.c323
-rw-r--r--sys/dev/idt/idtreg.h77
-rw-r--r--sys/dev/idt/idtvar.h228
5 files changed, 4699 insertions, 0 deletions
diff --git a/sys/dev/idt/idt.c b/sys/dev/idt/idt.c
new file mode 100644
index 0000000..9fc3cab
--- /dev/null
+++ b/sys/dev/idt/idt.c
@@ -0,0 +1,3307 @@
+/*
+ * 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.
+ *
+ ******************************************************************************
+ *
+ * $FreeBSD$
+ */
+
+#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>
+
+#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_page_alloc_contig(NICSTAR_FIXPAGES * PAGE_SIZE,
+ 0x100000, 0xffffffff, 0x2000);
+ if (idt->fixbuf == NULL)
+ 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 */
+
+ bzero((caddr_t)idt->fixbuf, NICSTAR_FIXPAGES * PAGE_SIZE);
+
+ /* 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 != NULL)
+ kmem_free(kernel_map, idt->fixbuf,
+ (NICSTAR_FIXPAGES * PAGE_SIZE));
+
+ if (idt->cbr_base != NULL)
+ 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
+ * a 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 = (struct mbuf **) vm_page_alloc_contig(size,
+ 0x100000, 0xffffffff, 0x2000);
+ 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_page_alloc_contig(pages * PAGE_SIZE,
+ 0x100000, 0xffffffff, 0x2000);
+#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 == NULL)
+ 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 = (CONNECTION *) vm_page_alloc_contig(pages * PAGE_SIZE,
+ 0x100000, 0xffffffff, 0x2000);
+ 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
+ * m_pkthdr.aux Unused, keep NULL
+ *
+ *******************************************************************************
+ *
+ * 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.aux != NULL) {
+ device_printf(idt->dev,
+ "received pkthdr.aux=%x\n",
+ (int)mptr->m_pkthdr.aux);
+ mptr->m_pkthdr.aux = 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;
+
+ idt = (IDT *) arg;
+
+ i = 0;
+
+ 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;
+ }
+ if (i < 1 || i > 50)
+ device_printf(idt->dev, "i=%3d, status=%08x\n", i, (int)stat_val);
+}
diff --git a/sys/dev/idt/idt_harp.c b/sys/dev/idt/idt_harp.c
new file mode 100644
index 0000000..ace90ef
--- /dev/null
+++ b/sys/dev/idt/idt_harp.c
@@ -0,0 +1,764 @@
+/*
+ * 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.
+ *
+ ******************************************************************************
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+
+#include <sys/bus.h>
+#include <sys/conf.h>
+
+#include <sys/module.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/netisr.h>
+#include <net/if_var.h>
+
+#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 <dev/idt/idtreg.h>
+#include <dev/idt/idtvar.h>
+
+/******************************************************************************
+ *
+ * HARP-specific definitions
+ *
+ */
+
+#define IDT_DEV_NAME "idt"
+
+#define IDT_IFF_MTU 9188
+#define IDT_MAX_VCI 1023 /* 0 - 1023 */
+#define IDT_MAX_VPI 0
+
+#define iv_next iv_cmn.cv_next
+#define iv_toku iv_cmn.cv_toku
+#define iv_upper iv_cmn.cv_upper
+#define iv_vccb iv_cmn.cv_connvc /* HARP 3.0 */
+#define iv_state iv_cmn.cv_state
+#define iu_pif iu_cmn.cu_pif
+#define iu_unit iu_cmn.cu_unit
+#define iu_flags iu_cmn.cu_flags
+#define iu_mtu iu_cmn.cu_mtu
+#define iu_open_vcc iu_cmn.cu_open_vcc
+#define iu_instvcc iu_cmn.cu_instvcc /* HARP 3.0 */
+#define iu_vcc iu_cmn.cu_vcc
+#define iu_vcc_zone iu_cmn.cu_vcc_zone
+#define iu_nif_zone iu_cmn.cu_nif_zone
+#define iu_ioctl iu_cmn.cu_ioctl
+#define iu_openvcc iu_cmn.cu_openvcc
+#define iu_closevcc iu_cmn.cu_closevcc
+#define iu_output iu_cmn.cu_output
+#define iu_config iu_cmn.cu_config
+
+/*
+ * ATM Interface services
+ */
+static struct stack_defn idt_svaal5 = {
+ NULL,
+ SAP_CPCS_AAL5,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+static struct stack_defn idt_svaal4 = {
+ &idt_svaal5,
+ SAP_CPCS_AAL3_4,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+static struct stack_defn idt_svaal0 = {
+ &idt_svaal4,
+ SAP_ATM,
+ SDF_TERM,
+ atm_dev_inst,
+ atm_dev_lower,
+ NULL,
+ 0,
+};
+struct stack_defn *idt_services = &idt_svaal0;
+
+extern uma_zone_t idt_nif_zone;
+extern uma_zone_t idt_vcc_zone;
+
+static int idt_atm_bearerclass(struct attr_bearer *);
+static CONNECTION *idt_atm_harpconn(Cmn_unit *, Cmn_vcc *);
+static int idt_atm_ioctl(int, caddr_t, caddr_t);
+
+static void idt_output(Cmn_unit *, Cmn_vcc *, KBuffer *);
+static int idt_openvcc(Cmn_unit *, Cmn_vcc *);
+static int idt_closevcc(Cmn_unit *, Cmn_vcc *);
+static int idt_instvcc(Cmn_unit *, Cmn_vcc *);
+
+static void idt_recv_stack(void *, KBuffer *);
+
+/******************************************************************************
+ *
+ * HARP GLUE SECTION
+ *
+ ******************************************************************************
+ *
+ * Handle netatm core service interface ioctl requests
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * code ioctl function (sub)code
+ * data data to/from ioctl
+ * arg optional code-specific argument
+ *
+ * Returns:
+ * 0 request processed successfully
+ * error request failed - reason code
+ *
+ */
+static int
+idt_atm_ioctl(int code, caddr_t addr, caddr_t arg)
+{
+#ifdef T_ATM_BUFQUEUE
+ CONNECTION *connection;
+ TX_QUEUE *txq;
+ struct mbuf *m;
+ Cmn_unit *cup;
+ Cmn_vcc *cvp;
+ int retval;
+#endif
+
+ switch (code) {
+
+#ifdef T_ATM_BUFQUEUE
+ case T_ATM_BUFQUEUE:
+ cup = (Cmn_unit *) addr;
+ cvp = (Cmn_vcc *) arg;
+ connection = idt_atm_harpconn(cup, cvp);
+ if (connection == NULL)
+ return (-1);
+ retval = 0;
+ txq = connection->queue;
+ if (txq == NULL)
+ return (-1);
+ for (m = txq->mget; m != NULL; m = m->m_nextpkt)
+ retval += m->m_pkthdr.len;
+ return (retval);
+#endif
+ }
+
+ return (ENOSYS);
+}
+
+/*******************************************************************************
+ *
+ * Get connection pointer from Cmn_unit and Cmn_vcc
+ *
+ * in: Cmn_unit and Cmn_vcc
+ * out: connection (NULL=error)
+ *
+ * Date first: 05/31/2001 last: 05/31/2001
+ */
+
+static CONNECTION *
+idt_atm_harpconn(Cmn_unit * cup, Cmn_vcc * cvp)
+{
+ struct vccb *vccinf; /* from HARP struct */
+ IDT *idt;
+ int vpi;
+ int vci;
+
+ idt = (IDT *) cup;
+ if (idt == NULL || cvp == NULL)
+ return (NULL);
+
+ if (cvp->cv_connvc == NULL)
+ return (NULL);
+
+ vccinf = cvp->cv_connvc->cvc_vcc;
+
+ if (vccinf == NULL)
+ return (NULL);
+
+ vpi = vccinf->vc_vpi;
+ vci = vccinf->vc_vci;
+
+ return (idt_connect_find(idt, vpi, vci));
+}
+
+/*******************************************************************************
+ *
+ * Get CBR/VBR/UBR class from bearer attribute
+ *
+ * in:
+ * out: NICCBR/NICVBR/NICABR/NICUBR
+ *
+ * Date first: 06/12/2001 last: 06/13/2001
+ */
+
+static int
+idt_atm_bearerclass(struct attr_bearer * bearer)
+{
+ switch (bearer->v.bearer_class) {
+ case T_ATM_CLASS_A:return (NICCBR);
+ case T_ATM_CLASS_C:
+ if (idt_sysctl_vbriscbr)
+ return (NICCBR); /* use CBR slots for VBR VC's */
+ else
+ return (NICVBR);
+ case T_ATM_CLASS_X:
+ if (bearer->v.traffic_type == T_ATM_CBR)
+ return (NICCBR);
+ if (bearer->v.traffic_type == T_ATM_VBR)
+ return (NICVBR);
+ return (NICUBR);
+ }
+ return (NICUBR);
+}
+
+/* The flag idt_sysctl_vbriscbr allows us to set up a CBR VC as if it were
+ * VBR. This is primarily to avoid cell loss at a switch that cannot seem
+ * to buffer one or two cells of jitter. This jitter is created when many
+ * CBR slots have been taken, and a new CBR VC cannot use the optimally
+ * spaced slots, and has to use nearby slots instead.
+ *
+ * In this case, we want to use the VC SCR as the CBR value. The PCR and MBS
+ * is only of interest to the switch.
+ *
+ *******************************************************************************
+ *
+ * Initialize HARP service
+ * called from device attach
+ */
+
+int
+idt_harp_init(nicstar_reg_t *idt)
+{
+ long long tsc_val;
+ u_char idt_mac[6];
+ int i;
+ int error;
+
+ error = 0;
+
+ /*
+ * Start initializing it
+ */
+ idt->iu_unit = device_get_unit(idt->dev);
+ idt->iu_mtu = IDT_IFF_MTU;
+ idt->iu_ioctl = idt_atm_ioctl;
+ idt->iu_openvcc = idt_openvcc;
+ idt->iu_instvcc = idt_instvcc;
+ idt->iu_closevcc = idt_closevcc;
+ idt->iu_output = idt_output;
+ idt->iu_vcc_zone = idt_vcc_zone;
+ idt->iu_nif_zone = idt_nif_zone;
+
+ /*
+ * Copy serial number into config space
+ */
+ idt->iu_config.ac_serial = 0;
+
+ idt->iu_config.ac_vendor = VENDOR_IDT;
+ idt->iu_config.ac_vendapi = VENDAPI_IDT_1;
+ idt->iu_config.ac_device = DEV_IDT_155;
+ idt->iu_config.ac_media = MEDIA_UNKNOWN;
+ idt->iu_config.ac_bustype = BUS_PCI;
+
+ idt->iu_pif.pif_pcr = idt->cellrate_rmax; /* ATM_PCR_OC3C; */
+ idt->iu_pif.pif_maxvpi = idt->conn_maxvpi;
+ idt->iu_pif.pif_maxvci = idt->conn_maxvci;
+
+ snprintf(idt->iu_config.ac_hard_vers,
+ sizeof(idt->iu_config.ac_hard_vers),
+ idt->hardware);
+ snprintf(idt->iu_config.ac_firm_vers,
+ sizeof(idt->iu_config.ac_firm_vers),
+ IDT_VERSION);
+ /*
+ * Save device ram info for user-level programs NOTE: This really
+ * points to start of EEPROM and includes all the device registers
+ * in the lower 2 Megabytes.
+ */
+ idt->iu_config.ac_ram = NULL;
+ idt->iu_config.ac_ramsize = 0;
+
+ for (i = 0; i < 6; i++) {
+ idt_mac[i] = nicstar_eeprom_rd(idt, (0x6c + i));
+ }
+
+ /* looks like bad MAC */
+ if ((idt_mac[3] | idt_mac[4] | idt_mac[5]) == 0) {
+ GET_RDTSC(tsc_val); /* 24 bits on 500mhz CPU is about
+ * 30msec */
+ idt_mac[0] = 0x00;
+ idt_mac[1] = 0x20;
+ idt_mac[2] = 0x48; /* use Fore prefix */
+ idt_mac[3] = (tsc_val >> 16) & 0xff;
+ idt_mac[4] = (tsc_val >> 8) & 0xff;
+ idt_mac[5] = (tsc_val) & 0xff;
+ device_printf(idt->dev,
+ "Cannot read MAC address from EEPROM, generating it.\n");
+ }
+ bcopy(&idt_mac, &idt->iu_pif.pif_macaddr.ma_data, sizeof(idt_mac));
+
+ device_printf(idt->dev, "MAC address %6D, HWrev=%d\n",
+ (u_int8_t *)&idt->iu_pif.pif_macaddr.ma_data, ":",
+ idt->pci_rev);
+
+ idt->iu_config.ac_macaddr = idt->iu_pif.pif_macaddr;
+
+ /*
+ * Register this interface with ATM core services
+ */
+ error = atm_physif_register(&idt->iu_cmn, IDT_DEV_NAME, idt_services);
+ if (error != 0) {
+ /*
+ * Registration failed - back everything out
+ */
+
+ log(LOG_ERR, "%s(): atm_physif_register failed\n", __func__);
+ return (error);
+ }
+ idt->iu_flags |= CUF_INITED;
+
+#if BSD >= 199506
+ /*
+ * Add hook to out shutdown function at_shutdown (
+ * (bootlist_fn)idt_pci_shutdown, idt, SHUTDOWN_POST_SYNC );
+ */
+#endif
+
+ return (error);
+}
+
+/*******************************************************************************
+ *
+ * Output data
+ */
+
+static void
+idt_output(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc, KBuffer * m)
+{
+ struct vccb *vccinf; /* from HARP struct */
+ IDT *idt;
+ int vpi;
+ int vci;
+ int flags;
+
+ idt = (IDT *) cmnunit;
+ flags = 0;
+
+ if (cmnvcc == NULL) {
+ device_printf(idt->dev, "idt_output arg error #1\n");
+ goto bad;
+ }
+ if (cmnvcc->cv_connvc == NULL) {
+ device_printf(idt->dev, "idt_output arg error #2\n");
+ goto bad;
+ }
+ vccinf = cmnvcc->cv_connvc->cvc_vcc;
+ if (vccinf == NULL) {
+ device_printf(idt->dev, "idt_output arg error #3\n");
+ goto bad;
+ }
+ vpi = vccinf->vc_vpi;
+ vci = vccinf->vc_vci;
+
+#ifdef CVF_MPEG2TS /* option to split bufs into small TS bufs */
+ if (cmnvcc->cv_flags & CVF_MPEG2TS)
+ flags = 1;
+#endif
+
+ idt_transmit(idt, m, vpi, vci, flags);
+
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+/*******************************************************************************
+ *
+ * Open VCC
+ */
+
+static int
+idt_openvcc(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc)
+{
+ Atm_attributes *attrib; /* from HARP struct */
+ struct vccb *vccinf; /* from HARP struct */
+ CONNECTION *connection;
+ IDT *idt;
+ int vpi;
+ int vci;
+ int class; /* NICCBR, NICVBR, or NICUBR */
+
+ idt = (IDT *) cmnunit;
+
+ if (cmnvcc == NULL || cmnvcc->cv_connvc == NULL) {
+ printf("idt_openvcc: bad request #1.\n");
+ return (1);
+ }
+ attrib = &cmnvcc->cv_connvc->cvc_attr;
+ vccinf = cmnvcc->cv_connvc->cvc_vcc;
+
+ if (attrib == NULL || vccinf == NULL) {
+ printf("idt_openvcc: bad request #2.\n");
+ return (1);
+ }
+ vpi = vccinf->vc_vpi;
+ vci = vccinf->vc_vci;
+
+ connection = idt_connect_find(idt, vpi, vci);
+ if (connection == NULL) {
+ printf("idt_openvcc: vpi/vci invalid: %d/%d\n", vpi, vci);
+ return (1);
+ }
+ if (connection->status) {
+ printf("idt_openvcc: connection already open %d/%d\n", vpi, vci);
+ return (1);
+ }
+ connection->status = 1;
+ connection->recv = NULL;
+ connection->rlen = 0;
+ connection->maxpdu = 20000;
+ connection->aal = IDTAAL5;
+ connection->traf_pcr = attrib->traffic.v.forward.PCR_all_traffic;
+ connection->traf_scr = attrib->traffic.v.forward.SCR_all_traffic;
+ connection->vccinf = vccinf; /* 12/15/2000 */
+
+ if (connection->traf_pcr <= 0)
+ connection->traf_pcr = connection->traf_scr;
+ if (connection->traf_scr <= 0)
+ connection->traf_scr = connection->traf_pcr;
+
+ class = idt_atm_bearerclass(&attrib->bearer);
+ if (vpi == 0 && vci == 5)
+ class = NICABR; /* higher priority than UBR */
+ if (vpi == 0 && vci == 16)
+ class = NICABR;
+
+ if (connection->traf_pcr < 0) { /* neither PCR nor SCR given */
+ connection->traf_pcr = 1;
+ connection->traf_scr = 1;
+ class = NICUBR; /* so give it lowest priority */
+ }
+ connection->class = class;
+
+ if (idt_connect_txopen(idt, connection)) {
+ device_printf(idt->dev, "cannot open connection for %d/%d\n",
+ vpi, vci);
+ return (1);
+ }
+ if (idt_sysctl_logvcs)
+ printf("idt_openvcc: %d/%d, PCR=%d, SCR=%d\n", vpi, vci,
+ connection->traf_pcr, connection->traf_scr);
+ idt_connect_opencls(idt, connection, 1); /* open entry in rcv
+ * connect table */
+
+ return (0);
+}
+
+/* We really don't handle ABR, but use it as a higher priority UBR. The
+ * idea is that a UBR connection that gives a PCR (like 0/16) should
+ * be given preference over a UBR connection that wants "everything else".
+ *
+ * Note that CLASS_X is typically UBR, but the traffic type information
+ * element may still specify CBR or VBR.
+ *
+ *******************************************************************************
+ *
+ * Close VCC
+ */
+
+static int
+idt_closevcc(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc)
+{
+ CONNECTION *connection;
+ nicstar_reg_t *idt = (nicstar_reg_t *) cmnunit;
+ int vpi;
+ int vci;
+
+ if (cmnvcc && cmnvcc->cv_connvc && cmnvcc->cv_connvc->cvc_vcc) {
+ vpi = cmnvcc->cv_connvc->cvc_vcc->vc_vpi;
+ vci = cmnvcc->cv_connvc->cvc_vcc->vc_vci;
+ } else {
+ printf("idt_closevcc: bad vcivpi\n");
+ return (0);
+ }
+ connection = idt_connect_find(idt, vpi, vci);
+
+ if (connection == NULL) {
+ printf("idt_closevcc: vpi/vci invalid: %d/%d\n", vpi, vci);
+ return (0);
+ }
+ idt_connect_opencls(idt, connection, 0); /* close entry in rcv
+ * connect table */
+
+ if (connection->status == 0)
+ printf("idt_closevcc: close on empty connection %d/%d\n", vpi, vci);
+ if (connection->recv != NULL)
+ m_freem(connection->recv); /* recycle mbuf of partial PDU */
+ idt_connect_txclose(idt, connection);
+ connection->status = 0;
+ connection->recv = NULL;
+ connection->rlen = 0;
+ connection->maxpdu = 0;
+ connection->aal = 0;
+ connection->traf_pcr = 0;
+ connection->traf_scr = 0;
+
+ if (idt_sysctl_logvcs)
+ printf("idt_closevcc: vpi=%d vci=%d\n", vpi, vci);
+
+ return (0);
+}
+
+/*
+ *
+ * VCC Stack Instantiation
+ *
+ * This function is called via the common driver code during a device VCC
+ * stack instantiation. The common code has already validated some of
+ * the request so we just need to check a few more IDT-specific details.
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 instantiation successful
+ * err instantiation failed - reason indicated
+ *
+ */
+static int
+idt_instvcc(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc)
+{
+ Atm_attributes *attrib; /* from HARP struct */
+ IDT *idt;
+ int class, pcr, scr;
+ int slots_vc, slots_cur, slots_max;
+
+ if (cmnvcc == NULL)
+ return (EINVAL);
+ if (cmnvcc->cv_connvc == NULL)
+ return (EINVAL);
+
+ idt = (IDT *) cmnunit;
+ if (idt == NULL)
+ return (EINVAL);
+
+ attrib = &cmnvcc->cv_connvc->cvc_attr;
+
+ if (attrib == NULL)
+ return (EINVAL);
+
+ pcr = attrib->traffic.v.forward.PCR_all_traffic;
+ scr = attrib->traffic.v.forward.SCR_all_traffic;
+
+ if (pcr <= 0)
+ pcr = scr; /* if PCR missing, default to SCR */
+ if (pcr <= 0)
+ pcr = 1;
+ if (scr <= 0)
+ scr = pcr;
+
+ class = idt_atm_bearerclass(&attrib->bearer);
+ if (class == NICCBR) {
+ slots_max = idt->txslots_max;
+ slots_cur = idt->txslots_cur;
+ slots_vc = idt_slots_cbr(idt, scr); /* 06/13/2001: now using
+ * SCR */
+ if (slots_vc + slots_cur > slots_max) {
+ if (idt_sysctl_logvcs)
+ device_printf(idt->dev,
+ "Insufficient bandwidth (vc=%d cur=%d max=%d)\n",
+ slots_vc, slots_cur, slots_max);
+ return (EINVAL);
+ }
+ }
+ /* This part was take from /sys/dev/hfa/fore_vcm.c */
+
+ switch (attrib->aal.type) {
+ case ATM_AAL0:
+ break;
+ case ATM_AAL3_4:
+ if ((attrib->aal.v.aal4.forward_max_SDU_size > IDT_IFF_MTU) ||
+ (attrib->aal.v.aal4.backward_max_SDU_size > IDT_IFF_MTU))
+ return (EINVAL);
+ break;
+ case ATM_AAL5:
+ if ((attrib->aal.v.aal5.forward_max_SDU_size > IDT_IFF_MTU) ||
+ (attrib->aal.v.aal5.backward_max_SDU_size > IDT_IFF_MTU))
+ return (EINVAL);
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*
+ * Pass Incoming PDU up Stack
+ *
+ * This function is called via the core ATM interrupt queue callback
+ * set in fore_recv_drain(). It will pass the supplied incoming
+ * PDU up the incoming VCC's stack.
+ *
+ * Called at splnet.
+ *
+ * Arguments:
+ * tok token to identify stack instantiation
+ * m pointer to incoming PDU buffer chain
+ *
+ * Returns:
+ * none
+ */
+static void
+idt_recv_stack(void *tok, KBuffer * m)
+{
+ Idt_vcc *ivp = (Idt_vcc *) tok;
+ int err;
+
+ if ((m->m_flags & M_PKTHDR) == 0) {
+ printf("idt_recv_stack: Warning - mbuf chain has no header.\n");
+ KB_FREEALL(m);
+ return;
+ }
+ /*
+ * Send the data up the stack
+ */
+ STACK_CALL(CPCS_UNITDATA_SIG, ivp->iv_upper,
+ ivp->iv_toku, ivp->iv_vccb, (int)m, 0, err);
+ if (err)
+ KB_FREEALL(m);
+
+ return;
+}
+
+/******************************************************************************
+ *
+ * Enqueue received PDU for HARP to handle
+ *
+ * in: IDT device, mbuf, vpi, vci
+ *
+ * Date last: 12/14/2000
+ */
+
+void
+idt_receive(nicstar_reg_t * idt, struct mbuf * m, int vpi, int vci)
+{
+ caddr_t cp;
+ Cmn_vcc *vcc;
+ int space;
+
+ /*
+ * The STACK_CALL needs to happen at splnet() in order for the stack
+ * sequence processing to work. Schedule an interrupt queue
+ * callback at splnet() since we are currently at device level.
+ */
+
+ /*
+ * Prepend callback function pointer and token value to buffer. We
+ * have already guaranteed that the space is available in the first
+ * buffer.
+ */
+
+ /*
+ * vcc = atm_dev_vcc_find(&idt->iu_cmn, (vpivci>> 16), vpivci &
+ * 0xffff, VCC_IN);
+ */
+
+ vcc = atm_dev_vcc_find(&idt->iu_cmn, vpi, vci, VCC_IN);
+
+ if (vcc == NULL) { /* harp stack not ready or no vcc */
+ printf("idt_receive: no VCC %d/%d\n", vpi, vci);
+ KB_FREEALL(m);
+ return;
+ }
+ space = m->m_data - idt_mbuf_base(m);
+ if (space < sizeof(atm_intr_func_t) + sizeof(int)) {
+ printf("idt_receive: NOT enough buffer space (%d).\n", space);
+ KB_FREEALL(m);
+ return;
+ }
+ KB_HEADADJ(m, sizeof(atm_intr_func_t) + sizeof(int));
+ KB_DATASTART(m, cp, caddr_t);
+ *((atm_intr_func_t *) cp) = idt_recv_stack;
+ cp += sizeof(atm_intr_func_t);
+
+ *((void **)cp) = (void *)vcc;
+
+ /*
+ * Schedule callback
+ */
+ if (!_IF_QFULL(&atm_intrq)) {
+ IF_ENQUEUE(&atm_intrq, m);
+ schednetisr(NETISR_ATM);
+ } else {
+ KB_FREEALL(m);
+ }
+}
diff --git a/sys/dev/idt/idt_pci.c b/sys/dev/idt/idt_pci.c
new file mode 100644
index 0000000..c2fe260
--- /dev/null
+++ b/sys/dev/idt/idt_pci.c
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <sys/bus.h>
+#include <sys/conf.h>
+
+#include <sys/module.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#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 <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/idt/idtreg.h>
+#include <dev/idt/idtvar.h>
+
+#define IDT_VID 0x111d
+#define IDT_NICSTAR_DID 0x0001
+
+struct pci_type {
+ u_int16_t pci_vid;
+ u_int16_t pci_did;
+ char * pci_name;
+} pci_devs[] = {
+ { IDT_VID, IDT_NICSTAR_DID, "IDT IDT77201/211 NICStAR ATM Adapter" },
+ { 0, 0, NULL }
+};
+
+uma_zone_t idt_nif_zone;
+uma_zone_t idt_vcc_zone;
+
+static int idt_probe (device_t);
+static int idt_attach (device_t);
+static int idt_detach (device_t);
+static int idt_shutdown (device_t);
+static void idt_free (device_t);
+static int idt_modevent (module_t, int, void *);
+
+static int
+idt_probe(device_t dev)
+{
+ struct pci_type *t = pci_devs;
+
+ while(t->pci_name != NULL) {
+ if ((pci_get_vendor(dev) == t->pci_vid) &&
+ (pci_get_device(dev) == t->pci_did)) {
+ device_set_desc(dev, t->pci_name);
+ return(0);
+ }
+ t++;
+ }
+
+ return(ENXIO);
+}
+
+/******************************************************************************
+ *
+ * Attach device
+ *
+ * Date first: 11/14/2000 last: 06/10/2001
+ */
+
+static int
+idt_attach(device_t dev)
+{
+ struct idt_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ error = 0;
+
+ pci_enable_busmaster(dev);
+
+ /* count = 2 (times 32 PCI clocks) */
+ pci_write_config(dev, PCIR_LATTIMER, 0x20, 1);
+
+ /* Map IDT registers */
+ sc->mem_rid = 0x14;
+ sc->mem_type = SYS_RES_MEMORY;
+ sc->mem = bus_alloc_resource(dev, sc->mem_type, &sc->mem_rid,
+ 0, ~0, 1, RF_ACTIVE);
+ if (sc->mem == NULL) {
+ device_printf(dev, "could not map registers.\n");
+ error = ENXIO;
+ goto fail;
+ }
+ sc->bustag = rman_get_bustag(sc->mem);
+ sc->bushandle = rman_get_bushandle(sc->mem);
+
+ /* Map interrupt */
+ sc->irq_rid = 0;
+ sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (sc->irq == NULL) {
+ device_printf(dev, "could not map interrupt.\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, nicstar_intr,
+ sc, &sc->irq_ih);
+ if (error) {
+ device_printf(dev, "could not setup irq.\n");
+ error = ENXIO;
+ goto fail;
+ }
+
+ sc->virt_baseaddr = (vm_offset_t)rman_get_virtual(sc->mem);
+ sc->cmd_reg = sc->virt_baseaddr + REGCMD; /* old reg */
+ sc->stat_reg = sc->virt_baseaddr + REGSTAT; /* old reg */
+ sc->reg_cmd = (u_long *)(sc->virt_baseaddr + REGCMD);
+ sc->reg_stat = (u_long *)(sc->virt_baseaddr + REGSTAT);
+ sc->reg_cfg = (u_long *)(sc->virt_baseaddr + REGCFG);
+ sc->reg_data = (u_long *)(sc->virt_baseaddr + 0);
+ sc->reg_tsqh = (u_long *)(sc->virt_baseaddr + REGTSQH);
+ sc->reg_gp = (u_long *)(sc->virt_baseaddr + REGGP);
+ sc->pci_rev = pci_get_revid(dev);
+ sc->timer_wrap = 0;
+
+ callout_handle_init(&sc->ch);
+
+ phys_init(sc); /* initialize the hardware */
+ nicstar_init(sc); /* allocate and initialize */
+
+ error = idt_harp_init(sc);
+ if (error)
+ goto fail;
+
+ return (0);
+fail:
+ idt_free(dev);
+ return (error);
+}
+
+/******************************************************************************
+ *
+ * Detach device
+ *
+ * Date first: 11/14/2000 last: 11/14/2000
+ */
+
+static int
+idt_detach(device_t dev)
+{
+ struct idt_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ error = 0;
+
+ /*
+ * De-Register this interface with ATM core services
+ */
+ error = atm_physif_deregister(&sc->iu_cmn);
+
+ idt_device_stop(sc); /* Stop the device */
+
+ /*
+ * Lock out all device interupts.
+ */
+ DEVICE_LOCK(&sc->iu_cmn);
+ idt_free(dev);
+ idt_release_mem(sc);
+ DEVICE_UNLOCK(&sc->iu_cmn);
+
+ return (error);
+}
+
+/******************************************************************************
+ *
+ * Shutdown device
+ *
+ * Date first: 11/14/2000 last: 11/14/2000
+ */
+
+static int
+idt_shutdown(device_t dev)
+{
+
+ struct idt_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ idt_device_stop(sc); /* Stop the device */
+
+ return (0);
+}
+
+static void
+idt_free (device_t dev)
+{
+ struct idt_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->irq_ih)
+ bus_teardown_intr(dev, sc->irq, sc->irq_ih);
+ if (sc->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+ if (sc->mem)
+ bus_release_resource(dev, sc->mem_type, sc->mem_rid, sc->mem);
+
+ return;
+}
+
+static int
+idt_modevent (module_t mod, int what, void *arg)
+{
+ int error;
+
+ error = 0;
+
+ switch (what) {
+ case MOD_LOAD:
+ idt_nif_zone = uma_zcreate("idt nif",
+ sizeof(struct atm_nif),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ if (idt_nif_zone == NULL)
+ panic("hfa_modevent:uma_zcreate nif");
+ uma_zone_set_max(idt_nif_zone, 20);
+
+ idt_vcc_zone = uma_zcreate("idt vcc",
+ sizeof(Idt_vcc),
+ NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+ if (idt_vcc_zone == NULL)
+ panic("hfa_modevent: uma_zcreate vcc");
+ uma_zone_set_max(idt_vcc_zone, 100);
+
+ break;
+
+ case MOD_UNLOAD:
+ uma_zdestroy(idt_nif_zone);
+ uma_zdestroy(idt_vcc_zone);
+
+ break;
+ default:
+ break;
+ }
+
+ return (error);
+}
+
+static device_method_t idt_methods[] = {
+ DEVMETHOD(device_probe, idt_probe),
+ DEVMETHOD(device_attach, idt_attach),
+ DEVMETHOD(device_detach, idt_detach),
+ DEVMETHOD(device_shutdown, idt_shutdown),
+ {0, 0}
+};
+
+static driver_t idt_driver = {
+ "idt",
+ idt_methods,
+ sizeof(struct idt_softc)
+};
+
+static devclass_t idt_devclass;
+
+DRIVER_MODULE(idt, pci, idt_driver, idt_devclass, idt_modevent, 0);
+MODULE_VERSION(idt, 1);
diff --git a/sys/dev/idt/idtreg.h b/sys/dev/idt/idtreg.h
new file mode 100644
index 0000000..43ede7e
--- /dev/null
+++ b/sys/dev/idt/idtreg.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ ******************************************************************************
+ *
+ * $FreeBSD$
+ */
+
+#define IDT_VERSION "IDT 1.101"
+#define CBR_VERBOSE 1 /* show CBR time slots */
+
+#define IDT_MAX_CBRSLOTS 2100 /* no smaller than value assigned to card */
+#define IDT_MAX_CBRQUEUE 64 /* max number of CBR connections: 1k each */
+
+/* AAL types */
+#define IDTAAL0 0
+#define IDTAAL1 1
+#define IDTAAL3_4 3
+#define IDTAAL5 5
+
+#define NICCBR 1
+#define NICVBR 2
+#define NICABR 3
+#define NICUBR 4
+
+/* NICStAR Operation Registers */
+#define REGCMD 0x10 /* command w */
+#define REGCFG 0x14 /* configuration r/w */
+#define REGSTAT 0x18 /* status r/w */
+#define REGRSQB 0x1c /* RSQ base w */
+#define REGRSQT 0x20 /* RSQ tail r */
+#define REGRSQH 0x24 /* RSQ head w */
+#define REGCDC 0x28 /* cell drop cnt r/c */
+#define REGVPEC 0x2c /* vci/vpi er cnt r/c */
+#define REGICC 0x30 /* invalid cell r/c */
+#define REGRAWT 0x34 /* raw cell tail r */
+#define REGTMR 0x38 /* timer r */
+#define REGTSTB 0x3c /* TST base r/w */
+#define REGTSQB 0x40 /* TSQ base w */
+#define REGTSQT 0x44 /* TSQ tail r */
+#define REGTSQH 0x48 /* TSQ head w */
+#define REGGP 0x4c /* general purp r/w */
+#define REGVMSK 0x50 /* vci/vpi mask w */
diff --git a/sys/dev/idt/idtvar.h b/sys/dev/idt/idtvar.h
new file mode 100644
index 0000000..16e0c45
--- /dev/null
+++ b/sys/dev/idt/idtvar.h
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ *
+ ******************************************************************************
+ *
+ * $FreeBSD$
+ */
+
+/*******************************************************************************
+ *
+ * New data types
+ */
+
+typedef struct {
+ struct mbuf *mget; /* head of mbuf queue, pull mbufs from here */
+ struct mbuf **mput; /* tail (ptr to m_nextpkt) put mbufs here */
+ u_long scd; /* segmentation channel descriptor address */
+ u_long *scq_base; /* segmentation channel queue base address */
+ u_long *scq_next; /* next address */
+ u_long *scq_last; /* last address written */
+ int scq_len; /* size of SCQ buffer (64 or 512) */
+ int scq_cur; /* current number entries in SCQ buffer */
+ int rate; /* cells per second allocated to this queue */
+ int vbr_m; /* VBR m/n = max duty cycle for queue */
+ int vbr_n; /* 1 <= m <= 7 and 1 <= n <= 127 */
+} TX_QUEUE;
+
+/* To avoid expensive SRAM reads, scq_cur tracks the number of SCQ entries
+ * in use. Only idt_transmit_top may increase this, and only idt_intr_tsq
+ * may decrease it.
+ */
+
+/* mbuf chains on the queue use the fields:
+ * m_next is the usual pointer to next mbuf
+ * m_nextpkt is the next packet on the queue
+ * m_pkthdr.rcvif is a pointer to the connection
+ * m_pkthdr.header is a pointer to the TX queue
+ */
+
+typedef struct {
+ struct vccb *vccinf;
+ char status; /* zero if closed */
+ char vpi;
+ u_short vci;
+ TX_QUEUE *queue; /* transmit queue for this connection */
+ struct mbuf *recv; /* current receive mbuf, or NULL */
+ int rlen; /* current receive length */
+ int maxpdu; /* largest PDU we will ever see */
+ int traf_pcr; /* peak cell rate */
+ int traf_scr; /* sustained cell rate */
+ u_char aal; /* AAL for this connection */
+ u_char class; /* T_ATM_CBR, T_ATM_VBR, or T_ATM_UBR */
+ u_char flg_mpeg2ts:1; /* send data as 2 TS == 8 AAL5 cells */
+ u_char flg_clp:1; /* CLP flag for outbound cells */
+} CONNECTION;
+
+#define MAX_CONNECTION 4096 /* max number of connections */
+
+#define GET_RDTSC(var) {__asm__ volatile("rdtsc":"=A"(var)); }
+
+/*******************************************************************************
+ *
+ * Device softc structure
+ */
+
+struct idt_softc {
+ /* HARP data */
+ /* XXX: must be first member of struct. */
+ Cmn_unit iu_cmn; /* Common unit stuff */
+
+#if 0
+ struct arpcom idt_ac; /* ifnet for device */
+#endif
+
+ /* Device data */
+ device_t dev;
+ int debug;
+
+ struct resource * mem;
+ int mem_rid;
+ int mem_type;
+ bus_space_tag_t bustag;
+ bus_space_handle_t bushandle;
+
+ struct resource * irq;
+ int irq_rid;
+ void * irq_ih;
+
+ struct callout_handle ch;
+
+ struct mtx mtx;
+
+ vm_offset_t virt_baseaddr; /* nicstar register virtual address */
+ vm_offset_t cmd_reg; /* command register offset 0x14 */
+ vm_offset_t stat_reg; /* status register offset 0x60 */
+ vm_offset_t fixbuf; /* buffer that holds TSQ, RSQ, variable SCQ */
+
+ u_long timer_wrap; /* keep track of wrapped timers */
+ u_long rsqh; /* Recieve Status Queue, reg is write-only */
+
+ CONNECTION *connection; /* connection table */
+ int conn_maxvpi; /* number of VPI values */
+ int conn_maxvci; /* number of VCI values */
+ int cellrate_rmax; /* max RX cells per second */
+ int cellrate_tmax; /* max TX cells per second */
+ int cellrate_rcur; /* current committed RX cellrate */
+ int cellrate_tcur; /* current committed TX cellrate */
+ int txslots_max; /* number of CBR TX slots for interface */
+ int txslots_cur; /* current CBR TX slots in use */
+ TX_QUEUE cbr_txqb[IDT_MAX_CBRQUEUE];
+ TX_QUEUE *cbr_slot[IDT_MAX_CBRSLOTS];
+ TX_QUEUE *cbr_free[IDT_MAX_CBRQUEUE];
+ TX_QUEUE queue_vbr;
+ TX_QUEUE queue_abr;
+ TX_QUEUE queue_ubr;
+ vm_offset_t cbr_base; /* base of memory for CBR TX queues */
+ int cbr_size; /* size of memory for CBR TX queues */
+ int cbr_freect;
+ u_long raw_headp; /* head of raw cell queue, physical */
+ struct mbuf *raw_headm; /* head of raw cell queue, virtual */
+ u_long *tsq_base; /* virtual TSQ base address */
+ u_long *tsq_head; /* virtual TSQ head pointer */
+ int tsq_size; /* number of TSQ entries (1024) */
+ volatile u_long *reg_cfg;
+ volatile u_long *reg_cmd;
+ volatile u_long *reg_data;
+ volatile u_long *reg_tsqh;
+ volatile u_long *reg_gp;
+ volatile u_long *reg_stat;
+ struct mbuf **mcheck;
+
+ int sram; /* amount of SRAM */
+ int pci_rev; /* hardware revision ID */
+ char *hardware; /* hardware description string */
+ u_char flg_le25:1; /* flag indicates LE25 instead of LE155 */
+ u_char flg_igcrc:1; /* ignore receive CRC errors */
+};
+
+typedef struct idt_softc nicstar_reg_t;
+typedef struct idt_softc IDT;
+
+#define iu_pif iu_cmn.cu_pif
+#define stats_ipdus iu_pif.pif_ipdus
+#define stats_opdus iu_pif.pif_opdus
+#define stats_ibytes iu_pif.pif_ibytes
+#define stats_obytes iu_pif.pif_obytes
+#define stats_ierrors iu_pif.pif_ierrors
+#define stats_oerrors iu_pif.pif_oerrors
+#define stats_cmderrors iu_pif.pif_cmderrors
+
+/*
+ * Device VCC Entry
+ *
+ * Contains the common and IDT-specific information for each VCC
+ * which is opened through a IDT device.
+ */
+struct nidt_vcc {
+ struct cmn_vcc iv_cmn; /* Common VCC stuff */
+};
+
+typedef struct nidt_vcc Idt_vcc;
+
+extern int idt_sysctl_logvcs;
+extern int idt_sysctl_vbriscbr;
+
+void nicstar_intr(void *);
+void phys_init(nicstar_reg_t * const);
+void nicstar_init(nicstar_reg_t * const);
+int idt_harp_init(nicstar_reg_t * const);
+void idt_device_stop(IDT *);
+void idt_release_mem(IDT *);
+
+CONNECTION *idt_connect_find(IDT *, int, int);
+caddr_t idt_mbuf_base(struct mbuf *);
+int idt_slots_cbr(IDT *, int);
+
+int idt_connect_opencls(IDT *, CONNECTION *, int);
+int idt_connect_txopen(IDT *, CONNECTION *);
+int idt_connect_txclose(IDT *, CONNECTION *);
+
+int nicstar_eeprom_rd(nicstar_reg_t * const, u_long);
+
+void idt_receive(IDT *, struct mbuf *, int, int);
+void idt_transmit(IDT *, struct mbuf *, int, int, int);
OpenPOWER on IntegriCloud