9d5abbddbf
especially in troff files.
3308 lines
90 KiB
C
3308 lines
90 KiB
C
/*
|
|
* 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
|
|
* an invalid queue is specified.
|
|
* returns 0 if parameters were acceptable.
|
|
*/
|
|
|
|
int
|
|
nicstar_add_buf(nicstar_reg_t * const idt, struct mbuf * buf0,
|
|
struct mbuf * buf1, u_long islrg)
|
|
{
|
|
u_long stat_val;
|
|
u_long val0, val1, val2, val3;
|
|
|
|
if (islrg > 1) /* bad buffer size */
|
|
return (-1);
|
|
|
|
stat_val = *idt->reg_stat;
|
|
|
|
if (islrg) {
|
|
if (stat_val & 0x80) /* large queue is full */
|
|
return (-1);
|
|
} else if (stat_val & 0x100) /* small queue is full */
|
|
return (-1);
|
|
|
|
if (!buf0 || !buf1 || ((u_long)(buf0->m_data) & 0x7)
|
|
|| ((u_long)(buf1->m_data) & 0x7)) {
|
|
return (-1); /* buffers must word aligned */
|
|
}
|
|
if (idt->raw_headm == NULL) /* raw cell buffer pointer not
|
|
* initialized */
|
|
if (islrg) {
|
|
idt->raw_headm = buf0;
|
|
idt->raw_headp = vtophys(buf0->m_data);
|
|
}
|
|
if (idt_status_wait(idt)) /* 12/06/2000 */
|
|
return (-1);
|
|
|
|
val0 = (u_long)buf0; /* mbuf address is handle */
|
|
val1 = vtophys(buf0->m_data); /* DMA addr of buff1 */
|
|
val2 = (u_long)buf1; /* mbuf address is handle */
|
|
val3 = vtophys(buf1->m_data); /* DMA addr of buff2 */
|
|
|
|
*(idt->reg_data + 0) = val0;
|
|
*(idt->reg_data + 1) = val1;
|
|
*(idt->reg_data + 2) = val2;
|
|
*(idt->reg_data + 3) = val3;
|
|
|
|
*idt->reg_cmd = 0x60000000 | islrg;
|
|
|
|
idt_mcheck_add(idt, buf0);
|
|
idt_mcheck_add(idt, buf1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* nicstar_util_rd ( card, util_location, answer_pointer )
|
|
*
|
|
* reads one byte from the utility bus at "util_location" and places the
|
|
* value in "answer_pointer"
|
|
*
|
|
* returns -1 if util location is out of range.
|
|
* returns 0 if parameters were acceptable
|
|
*/
|
|
static int
|
|
nicstar_util_rd(nicstar_reg_t * const idt, u_long address, u_long *data)
|
|
{
|
|
|
|
if (address >= 0x81) /* bad address */
|
|
return (-1);
|
|
|
|
if (idt_status_wait(idt))
|
|
return (-1);
|
|
|
|
*idt->reg_cmd = 0x80000200 | address; /* read a word */
|
|
|
|
if (idt_status_wait(idt))
|
|
return (-1);
|
|
|
|
*data = *idt->reg_data & 0xff; /* save word */
|
|
|
|
return (0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* nicstar_util_wr ( card, util location, data )
|
|
*
|
|
* writes one byte to the utility bus at "util_location"
|
|
*
|
|
* returns -1 if util location is out of range.
|
|
* returns 0 if parameters were acceptable
|
|
*/
|
|
static int
|
|
nicstar_util_wr(nicstar_reg_t * const idt, int cs, u_long address, u_long data)
|
|
{
|
|
|
|
if (address >= 0x81) /* bad address */
|
|
return (-1);
|
|
if (cs > 1)
|
|
return (-1);
|
|
|
|
if (idt_status_wait(idt))
|
|
return (-1);
|
|
|
|
*idt->reg_data = data & 0xff; /* load last data item */
|
|
|
|
if (cs == 0)
|
|
*idt->reg_cmd = 0x90000100 | address; /* write the byte, CS1 */
|
|
else
|
|
*idt->reg_cmd = 0x90000200 | address; /* write the byte, CS2 */
|
|
|
|
return (0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* nicstar_eeprom_rd ( card , byte_location )
|
|
*
|
|
* reads one byte from the utility bus at "byte_location" and return the
|
|
* value as an integer. this routint is only used to read the MAC address
|
|
* from the EEPROM at boot time.
|
|
*/
|
|
int
|
|
nicstar_eeprom_rd(nicstar_reg_t * const idt, u_long address)
|
|
{
|
|
volatile u_long *regGP = (volatile u_long *)(idt->virt_baseaddr + REGGP);
|
|
volatile u_long gp = *regGP & 0xfffffff0;
|
|
int i, value = 0;
|
|
|
|
DELAY(5); /* make sure idle */
|
|
*regGP = gp | 0x06; /* CS and Clock high */
|
|
DELAY(5);
|
|
*regGP = gp; /* CS and Clock low */
|
|
DELAY(5);
|
|
/* toggle in READ CMD (00000011) */
|
|
*regGP = gp | 0x04; /* Clock high (data 0) */
|
|
DELAY(5);
|
|
*regGP = gp; /* CS and Clock low */
|
|
DELAY(5);
|
|
*regGP = gp | 0x04; /* Clock high (data 0) */
|
|
DELAY(5);
|
|
*regGP = gp; /* CS and Clock low */
|
|
DELAY(5);
|
|
*regGP = gp | 0x04; /* Clock high (data 0) */
|
|
DELAY(5);
|
|
*regGP = gp; /* CS and Clock low */
|
|
DELAY(5);
|
|
*regGP = gp | 0x04; /* Clock high (data 0) */
|
|
DELAY(5);
|
|
*regGP = gp; /* CS and Clock low */
|
|
DELAY(5);
|
|
*regGP = gp | 0x04; /* Clock high (data 0) */
|
|
DELAY(5);
|
|
*regGP = gp; /* CS and Clock low */
|
|
DELAY(5);
|
|
*regGP = gp | 0x04; /* Clock high (data 0) */
|
|
DELAY(5);
|
|
*regGP = gp | 0x01; /* CS and Clock low data 1 */
|
|
DELAY(5);
|
|
*regGP = gp | 0x05; /* Clock high (data 1) */
|
|
DELAY(5);
|
|
*regGP = gp | 0x01; /* CS and Clock low data 1 */
|
|
DELAY(5);
|
|
*regGP = gp | 0x05; /* Clock high (data 1) */
|
|
DELAY(5);
|
|
/* toggle in the address */
|
|
for (i = 7; i >= 0; i--) {
|
|
*regGP = (gp | ((address >> i) & 1)); /* Clock low */
|
|
DELAY(5);
|
|
*regGP = (gp | 0x04 | ((address >> i) & 1)); /* Clock high */
|
|
DELAY(5);
|
|
}
|
|
/* read EEPROM data */
|
|
for (i = 7; i >= 0; i--) {
|
|
*regGP = gp; /* Clock low */
|
|
DELAY(5);
|
|
value |= ((*regGP & 0x10000) >> (16 - i));
|
|
*regGP = gp | 0x04; /* Clock high */
|
|
DELAY(5);
|
|
}
|
|
*regGP = gp; /* CS and Clock low */
|
|
return (value);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Load the card receive buffers
|
|
*
|
|
* in: IDT device
|
|
*
|
|
* Date first: 11/01/2000 last: 05/25/2000
|
|
*/
|
|
|
|
void
|
|
nicstar_ld_rcv_buf(IDT * idt)
|
|
{
|
|
struct mbuf *m1, *m2;
|
|
u_long stat_reg;
|
|
int card_small;
|
|
int card_large;
|
|
int s;
|
|
|
|
s = splimp();
|
|
|
|
stat_reg = *(volatile u_long *)idt->stat_reg;
|
|
|
|
card_small = (stat_reg & 0xff000000) >> 23; /* reg is number of
|
|
* pairs */
|
|
card_large = (stat_reg & 0x00ff0000) >> 15;
|
|
|
|
if (idt_sysctl_bufsmall > 510)
|
|
idt_sysctl_bufsmall = 510;
|
|
if (idt_sysctl_buflarge > 510)
|
|
idt_sysctl_buflarge = 510;
|
|
if (idt_sysctl_bufsmall < 10)
|
|
idt_sysctl_bufsmall = 10;
|
|
if (idt_sysctl_buflarge < 10)
|
|
idt_sysctl_buflarge = 10;
|
|
|
|
while (card_small < idt_sysctl_bufsmall) { /* 05/25/2001 from fixed */
|
|
MGETHDR(m1, M_DONTWAIT, MT_DATA);
|
|
if (m1 == NULL)
|
|
break;
|
|
MGETHDR(m2, M_DONTWAIT, MT_DATA);
|
|
if (m2 == NULL) {
|
|
m_free(m1);
|
|
break;
|
|
}
|
|
MH_ALIGN(m1, 96); /* word align & allow lots of
|
|
* prepending */
|
|
MH_ALIGN(m2, 96);
|
|
if (nicstar_add_buf(idt, m1, m2, 0)) {
|
|
device_printf(idt->dev,
|
|
"Cannot add small buffers, size=%d.\n",
|
|
card_small);
|
|
m_free(m1);
|
|
m_free(m2);
|
|
break;
|
|
}
|
|
card_small += 2;
|
|
}
|
|
|
|
while (card_large < idt_sysctl_buflarge) { /* 05/25/2001 from fixed */
|
|
m1 = idt_mbufcl_get();
|
|
if (m1 == NULL)
|
|
break;
|
|
m2 = idt_mbufcl_get();
|
|
if (m2 == NULL) {
|
|
m_free(m1);
|
|
break;
|
|
}
|
|
if (nicstar_add_buf(idt, m1, m2, 1)) {
|
|
device_printf(idt->dev,
|
|
"Cannot add large buffers, size=%d.\n",
|
|
card_large);
|
|
m_free(m1);
|
|
m_free(m2);
|
|
break;
|
|
}
|
|
card_large += 2;
|
|
}
|
|
idt_sysctl_curlarge = card_large;
|
|
idt_sysctl_cursmall = card_small;
|
|
|
|
splx(s);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Wait for command to finish
|
|
*
|
|
* in: IDT device
|
|
* out: zero = success
|
|
*
|
|
* Date first: 12/06/2000 last: 12/16/2000
|
|
*/
|
|
|
|
int
|
|
idt_status_wait(IDT * idt)
|
|
{
|
|
int timeout;
|
|
|
|
timeout = 33 * 100; /* allow 100 microseconds timeout */
|
|
|
|
while (*idt->reg_stat & 0x200)
|
|
if (--timeout == 0) {
|
|
device_printf(idt->dev,
|
|
"timeout waiting for device status.\n");
|
|
idt->stats_cmderrors++;
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Log status of system buffers
|
|
*
|
|
* in: IDT device
|
|
*
|
|
* Date first: 10/31/2000 last: 05/25/2001
|
|
*/
|
|
|
|
void
|
|
idt_status_bufs(IDT * idt)
|
|
{
|
|
u_long stat_reg;
|
|
int card_small;
|
|
int card_large;
|
|
int s;
|
|
|
|
s = splimp();
|
|
|
|
stat_reg = *(volatile u_long *)idt->stat_reg;
|
|
|
|
card_small = (stat_reg & 0xff000000) >> 23; /* reg is number of
|
|
* pairs */
|
|
card_large = (stat_reg & 0x00ff0000) >> 15;
|
|
|
|
splx(s);
|
|
|
|
device_printf(idt->dev, "BUFFER STATUS: small=%d/%d, large=%d/%d.\n",
|
|
card_small, idt_sysctl_bufsmall,
|
|
card_large, idt_sysctl_buflarge);
|
|
}
|
|
|
|
/* Since this is called when the card timer wraps, we should only see
|
|
* this 16 times (LE155) or 10 (LE25) per hour.
|
|
*
|
|
*******************************************************************************
|
|
*
|
|
* Add mbuf into "owned" list
|
|
*
|
|
* in: IDT device, mbuf
|
|
* out: zero = success
|
|
*
|
|
* Date first: 11/13/2000 last: 11/13/2000
|
|
*/
|
|
|
|
int
|
|
idt_mcheck_add(IDT * idt, struct mbuf * m)
|
|
{
|
|
int hpos;
|
|
int s;
|
|
|
|
hpos = (((int)m) >> 8) & 1023;
|
|
s = splimp();
|
|
|
|
m->m_next = idt->mcheck[hpos];
|
|
idt->mcheck[hpos] = m;
|
|
|
|
splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Remove mbuf from "owned" list
|
|
*
|
|
* in: IDT device, mbuf
|
|
* out: zero = success
|
|
*
|
|
* Date first: 11/13/2000 last: 11/13/2000
|
|
*/
|
|
|
|
int
|
|
idt_mcheck_rem(IDT * idt, struct mbuf * m)
|
|
{
|
|
struct mbuf *nbuf;
|
|
int hpos;
|
|
int s;
|
|
|
|
hpos = (((int)m) >> 8) & 1023;
|
|
s = splimp();
|
|
|
|
nbuf = idt->mcheck[hpos];
|
|
|
|
if (nbuf == m) {
|
|
idt->mcheck[hpos] = m->m_next;
|
|
splx(s);
|
|
m->m_next = NULL;
|
|
return (0);
|
|
}
|
|
while (nbuf != NULL) {
|
|
if (nbuf->m_next != m) {
|
|
nbuf = nbuf->m_next;
|
|
continue;
|
|
}
|
|
nbuf->m_next = m->m_next;
|
|
splx(s);
|
|
m->m_next = NULL;
|
|
return (0);
|
|
}
|
|
|
|
splx(s);
|
|
device_printf(idt->dev, "Card should not have this mbuf! %x\n", (int)m);
|
|
return (1);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* Initialize mbuf "owned" list
|
|
*
|
|
* in: IDT device
|
|
* out: zero = success
|
|
*
|
|
* Date first: 11/13/2000 last: 05/26/2001
|
|
*/
|
|
|
|
int
|
|
idt_mcheck_init(IDT * idt)
|
|
{
|
|
int size;
|
|
int x;
|
|
|
|
size = round_page(sizeof(struct mbuf *) * 1024);
|
|
idt->mcheck = (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);
|
|
}
|