diff options
author | mdodd <mdodd@FreeBSD.org> | 2002-09-30 05:12:39 +0000 |
---|---|---|
committer | mdodd <mdodd@FreeBSD.org> | 2002-09-30 05:12:39 +0000 |
commit | eb286245fd42ae3932bf7b3ed6c7b5aaa4fed3d8 (patch) | |
tree | e2a3574008786c5ff9f08b7aaaab6f74a7dd910c | |
parent | 13a6ca1e93fbdcf204c248b4e23b94418443dc9e (diff) | |
download | FreeBSD-src-eb286245fd42ae3932bf7b3ed6c7b5aaa4fed3d8.zip FreeBSD-src-eb286245fd42ae3932bf7b3ed6c7b5aaa4fed3d8.tar.gz |
HARP driver for the IDT77201/211 NICStAR ATM Adapter (Including Fore LE155).
Obtained from: Richard Hodges <rh@matriplex.com>
-rw-r--r-- | sys/dev/idt/idt.c | 3307 | ||||
-rw-r--r-- | sys/dev/idt/idt_harp.c | 764 | ||||
-rw-r--r-- | sys/dev/idt/idt_pci.c | 323 | ||||
-rw-r--r-- | sys/dev/idt/idtreg.h | 77 | ||||
-rw-r--r-- | sys/dev/idt/idtvar.h | 228 | ||||
-rw-r--r-- | sys/modules/idt/Makefile | 9 |
6 files changed, 4708 insertions, 0 deletions
diff --git a/sys/dev/idt/idt.c b/sys/dev/idt/idt.c new file mode 100644 index 0000000..9fc3cab --- /dev/null +++ b/sys/dev/idt/idt.c @@ -0,0 +1,3307 @@ +/* + * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Matriplex, inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * This driver is derived from the Nicstar driver by Mark Tinguely, and + * some of the original driver still exists here. Those portions are... + * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely + * All rights reserved. + * + ****************************************************************************** + * + * This driver supports the Fore LE155, LE25, and IDT 77211 cards. + * + * ATM CBR connections are supported, and bandwidth is allocated in + * slots of 64k each. Three VBR queues handle traffic for VBR and + * UBR. Two UBR queues prioritize UBR traffic. ILMI and signalling + * get the higher priority queue, as well as UBR traffic that specifies + * a peak cell rate. All other UBR traffic goes into the lower queue. + * + ****************************************************************************** + * + * The following sysctl variables are used: + * + * hw.idt.log_bufstat (0) Log free buffers (every few minutes) + * hw.idt.log_vcs (0) Log VC opens, closes, and other events + * hw.idt.bufs_large (100) Max/target number of free 2k buffers + * hw.idt.bufs_small (200) Max/target number of free mbufs + * hw.idt.cur_large (R/O) Current number of free 2k buffers + * hw.idt.cur_small (R/O) Current number of free mbufs + * hw.idt.qptr_hold (1) Optimize TX queue buffer for lowest overhead + * + * Note that the read-only buffer counts will not work with multiple cards. + * + ****************************************************************************** + * + * Assumptions: + * + * 1. All mbuf clusters are 2048 bytes, and aligned. + * 2. All mbufs are 256 bytes, and aligned (see idt_intr_tsq). + * + * Bugs: + * + * 1. Function idt_detach() is unusuable because idt_release_mem() is + * incomplete. The mbufs held in the free buffer queues can be + * recovered from the "mcheck" hash table. + * 2. The memory allocation could be cleaned up quite a bit. + * + ****************************************************************************** + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <sys/sockio.h> + +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/mman.h> +#include <machine/clock.h> +#include <machine/cpu.h> /* bootverbose */ + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <net/if.h> +#include <net/if_arp.h> + +#if MCLBYTES != 2048 +#error "This nicstar driver depends on 2048 byte mbuf clusters." +#endif + +#include <netatm/port.h> +#include <netatm/queue.h> +#include <netatm/atm.h> +#include <netatm/atm_sys.h> +#include <netatm/atm_sap.h> +#include <netatm/atm_cm.h> +#include <netatm/atm_if.h> +#include <netatm/atm_stack.h> +#include <netatm/atm_pcb.h> +#include <netatm/atm_var.h> +#include <netatm/atm_vc.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#include <dev/idt/idtreg.h> +#include <dev/idt/idtvar.h> + +#define MAXCARDS 10 /* set to impossibly high */ + +/****************************************************************************** + * + * You may change IDT_LBUFS and IDT_SBUFS if you wish. + */ + +#define NICSTAR_LRG_SIZE 2048 /* must be power of two */ +#define IDT_LBUFS 100 /* default number of 2k buffers */ +#define IDT_SBUFS 200 /* default number of 96-byte buffers */ + +#define IDT_TST_START 0x1c000 /* transmit schedule table start */ +#define IDT_SCD_START 0x1d000 /* segmentation channel descriptors start */ +#define IDT_SCD_SIZE 509 /* max number of SCD entries */ + +#define NICSTAR_FIXPAGES 10 + +static int idt_sysctl_logbufs = 0; /* periodic buffer status messages */ + int idt_sysctl_logvcs = 0; /* log VC open & close events */ +static int idt_sysctl_buflarge = IDT_LBUFS; /* desired large buffer queue */ +static int idt_sysctl_bufsmall = IDT_SBUFS; /* desired small buffer queue */ +static int idt_sysctl_curlarge = 0; /* current large buffer queue */ +static int idt_sysctl_cursmall = 0; /* current small buffer queue */ +static int idt_sysctl_qptrhold = 1; /* hold TX queue pointer back */ + int idt_sysctl_vbriscbr = 0; /* use CBR slots for VBR VC's */ + +SYSCTL_NODE(_hw, OID_AUTO, idt, CTLFLAG_RW, 0, "IDT Nicstar"); + +SYSCTL_INT(_hw_idt, OID_AUTO, log_bufstat, CTLFLAG_RW, + &idt_sysctl_logbufs, 0, "Log buffer status"); +SYSCTL_INT(_hw_idt, OID_AUTO, log_vcs, CTLFLAG_RW, + &idt_sysctl_logvcs, 0, "Log VC open/close"); + +SYSCTL_INT(_hw_idt, OID_AUTO, bufs_large, CTLFLAG_RW, + &idt_sysctl_buflarge, IDT_LBUFS, "Large buffer queue"); +SYSCTL_INT(_hw_idt, OID_AUTO, bufs_small, CTLFLAG_RW, + &idt_sysctl_bufsmall, IDT_SBUFS, "Small buffer queue"); +SYSCTL_INT(_hw_idt, OID_AUTO, cur_large, CTLFLAG_RD, + &idt_sysctl_curlarge, 0, "Current large queue"); +SYSCTL_INT(_hw_idt, OID_AUTO, cur_small, CTLFLAG_RD, + &idt_sysctl_cursmall, 0, "Current small queue"); +SYSCTL_INT(_hw_idt, OID_AUTO, qptr_hold, CTLFLAG_RW, + &idt_sysctl_qptrhold, 1, "Optimize TX queue ptr"); +SYSCTL_INT(_hw_idt, OID_AUTO, vbr_is_cbr, CTLFLAG_RW, + &idt_sysctl_vbriscbr, 0, "Use CBR for VBR VC's"); + +/****************************************************************************** + * + * common VCI values + * + * 0/0 Idle cells + * 0/1 Meta signalling + * x/1 Meta signalling + * 0/2 Broadcast signalling + * x/2 Broadcast signalling + * x/3 Segment OAM F4 flow + * x/4 End-end OAM F4 flow + * 0/5 p-p signalling + * x/5 p-p signalling + * x/6 rate management + * 0/14 SPANS + * 0/15 SPANS + * 0/16 ILMI + * 0/18 PNNI + */ + +/******************************************************************************* + * + * fixbuf memory map: + * + * 0000 - 1fff: TSQ Transmit status queue 1024 entries * 8 bytes each + * 2000 - 3fff: RSQ Receive status queue, 512 entries * 16 bytes each + * 4000 - 5fff: VBR segmentation channel queue (highest priority) + * 6000 - 7fff: ABR segmentation channel queue (middle priority) + * 8000 - 9fff: UBR segmentation channel queue (lowest priority) + * + * IDT device memory map: + * + * 1fc00: RX large buffer queue (4k) + * 1f800: RX small buffer queue (4k) + * 1e800: RX cells FIFO (16k) + * 1e7f4: SCD0 - VBR (12) + * 1e7e8: SCD1 - ABR (12) + * 1e7dc: SCD2 - UBR (12) + * 1e7db: CBR SCD end (last word) + * 1d000: CBR SCD start (509 entries) + * 1cfff: TST end (4095 available slots) + * 1c000: TST start (first CBR slot) + * + */ + +static u_long idt_found = 0; + + /* -------- buffer management -------- */ +static int nicstar_sram_wr(nicstar_reg_t * const, u_long, + int, u_long, u_long, u_long, u_long); +static int nicstar_sram_rd(nicstar_reg_t * const, u_long, u_long *); +static int nicstar_add_buf(nicstar_reg_t * const, struct mbuf *, + struct mbuf *, u_long); +static int nicstar_util_rd(nicstar_reg_t * const, u_long, u_long *); +static int nicstar_util_wr(nicstar_reg_t * const, int, u_long, u_long); + void nicstar_ld_rcv_buf(nicstar_reg_t * const); + + /* -------- interface routines -------- */ +int nicstar_output(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +void nicstar_start(struct ifnet *); + + /* -------- VCC open/close routines -------- */ +static void nicstar_itrx(nicstar_reg_t *); + + /* -------- receiving routines -------- */ +static void nicstar_rawc(nicstar_reg_t *); +static void nicstar_recv(nicstar_reg_t *); +static void nicstar_phys(nicstar_reg_t *); + +/******************************************************************************* + * + * New functions + */ + +static int idt_buffer_init(IDT *); +static struct mbuf *idt_mbufcl_get(void); + +static int idt_connect_init(IDT *, int); +static void idt_connect_newvbr(IDT *); + +static void idt_intr_tsq(IDT *); + +static vm_offset_t idt_malloc_contig(int); + +static int idt_mbuf_align(struct mbuf *, struct mbuf *); +static int idt_mbuf_append4(struct mbuf *, char *); +static struct mbuf *idt_mbuf_copy(IDT *, struct mbuf *); +static int idt_mbuf_prepend(struct mbuf *, char *, int); +static int idt_mbuf_used(struct mbuf *); + +static int idt_mcheck_add(IDT *, struct mbuf *); +static int idt_mcheck_rem(IDT *, struct mbuf *); +static int idt_mcheck_init(IDT *); + +static int idt_queue_flush(CONNECTION *); +static struct mbuf *idt_queue_get(TX_QUEUE *); +static int idt_queue_init(IDT *); +static int idt_queue_put(CONNECTION *, struct mbuf *); + +static int idt_receive_aal5(IDT *, struct mbuf *, struct mbuf *); +static void idt_transmit_drop(IDT *, struct mbuf *); +static void idt_transmit_top(IDT *, TX_QUEUE *); + +static int idt_slots_add(IDT *, TX_QUEUE *, int); +static int idt_slots_init(IDT *); +static int idt_slots_rem(IDT *, TX_QUEUE *); + +static int idt_phys_detect(IDT *); +static void idt_status_bufs(IDT *); +static int idt_status_wait(IDT *); + +/****************************************************************************** + * + * VBR queue divisor table + */ + +static unsigned char vbr_div_m[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, + 1, 2, 2, 2, 3, 1, 2, 1, 3, 2, 3, 3, 4, 3, 3, 2, 4, 1, 3, 3, + 1, 5, 5, 4, 4, 5, 4, 4, 6, 5, 1, 5, 4, 6, 2, 6, 7, 7, 4, 1, + 3, 5, 7, 7, 5, 5, 7, 7, 7, 2, 7, 7, 7, 7, 2, 3, 6, 1, 6, 3, + 2, 3, 5, 1, 7, 4, 5, 2, 3, 4, 7, 1, 7, 4, 3, 2, 7, 7, 5, 7, + 1, 7, 5, 7, 5, 2, 7, 3, 4, 6, 7, 1, 1, 7, 4, 7, 5, 7, 2, 5, + 3, 4, 5, 7, 1, 1, 1, 7, 5, 4, 7, 3, 7, 2, 7, 5, 3, 7, 4, 5, + 7, 7, 1, 1, 1, 7, 7, 5, 4, 7, 3, 5, 7, 7, 2, 7, 5, 5, 3, 7, + 4, 5, 6, 7, 7, 1, 1, 1, 1, 7, 7, 7, 5, 5, 4, 7, 3, 3, 5, 5, + 7, 2, 2, 2, 7, 5, 5, 3, 3, 7, 4, 4, 5, 6, 7, 7, 7, 7, 1, 1, + 1, 1, 1, 7, 7, 7, 7, 6, 5, 5, 4, 4, 7, 7, 3, 3, 5, 5, 5, 7, + 7, 2, 2, 2, 2, 7, 7, 5, 5, 5, 3, 3, 3, 7, 7, 4, 4, 5, 5, 5, + 6, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, + 7, 6, 6, 5, 5, 4, 4, 4, 7, 7, 7, 3, 3, 3, 3, 3, 5, 5, 5, 7, + 7, 7, 7, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 5, 5, 5, 5, 5, 3, 3, + 3, 3, 3, 7, 7, 7, 7, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, + 5, 4, 4, 4, 4, 4, 4, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, + 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, + 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +static unsigned char vbr_div_n[] = { + 127, 127, 127, 127, 127, 127, 127, 127, 125, 111, 100, 91, 83, 77, 71, + 67, 125, 59, 111, 105, 50, 95, 91, 87, 125, 40, 77, 37, 107, 69, + 100, 97, 125, 91, 88, 57, 111, 27, 79, 77, 25, 122, 119, 93, 91, + 111, 87, 85, 125, 102, 20, 98, 77, 113, 37, 109, 125, 123, 69, 17, + 50, 82, 113, 111, 78, 77, 106, 104, 103, 29, 100, 99, 97, 96, 27, + 40, 79, 13, 77, 38, 25, 37, 61, 12, 83, 47, 58, 23, 34, 45, + 78, 11, 76, 43, 32, 21, 73, 72, 51, 71, 10, 69, 49, 68, 48, + 19, 66, 28, 37, 55, 64, 9, 9, 62, 35, 61, 43, 60, 17, 42, + 25, 33, 41, 57, 8, 8, 8, 55, 39, 31, 54, 23, 53, 15, 52, + 37, 22, 51, 29, 36, 50, 50, 7, 7, 7, 48, 48, 34, 27, 47, + 20, 33, 46, 46, 13, 45, 32, 32, 19, 44, 25, 31, 37, 43, 43, + 6, 6, 6, 6, 41, 41, 41, 29, 29, 23, 40, 17, 17, 28, 28, + 39, 11, 11, 11, 38, 27, 27, 16, 16, 37, 21, 21, 26, 31, 36, + 36, 36, 36, 5, 5, 5, 5, 5, 34, 34, 34, 34, 29, 24, 24, + 19, 19, 33, 33, 14, 14, 23, 23, 23, 32, 32, 9, 9, 9, 9, + 31, 31, 22, 22, 22, 13, 13, 13, 30, 30, 17, 17, 21, 21, 21, + 25, 29, 29, 29, 29, 29, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 27, 27, 27, 27, 27, 27, 23, 23, 19, 19, 15, 15, 15, 26, 26, + 26, 11, 11, 11, 11, 11, 18, 18, 18, 25, 25, 25, 25, 7, 7, + 7, 7, 7, 7, 24, 24, 24, 24, 17, 17, 17, 17, 17, 10, 10, + 10, 10, 10, 23, 23, 23, 23, 13, 13, 13, 13, 16, 16, 16, 16, + 19, 19, 22, 22, 22, 22, 22, 22, 22, 22, 22, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 17, 17, 17, 17, 14, 14, 14, 14, + 14, 11, 11, 11, 11, 11, 11, 19, 19, 19, 19, 19, 8, 8, 8, + 8, 8, 8, 8, 8, 13, 13, 13, 13, 13, 13, 13, 18, 18, 18, + 18, 18, 18, 18, 18, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 17, 17, 17, 17, 17, 17, 17, 17, 17, 12, 12, 12, 12, 12, + 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 16, + 16, 16, 16, 16, 16, 16, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 11, 11, 11, 11, 11, 11, 11, 11, 11, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +/****************************************************************************** + * + * Stop the device (shutdown) + * + * in: IDT device + * + * Date first: 11/14/2000 last: 11/14/2000 + */ + +void +idt_device_stop(IDT * idt) +{ + u_long val; + int s; + + s = splimp(); + + *(idt->reg_cfg) = 0x80000000; /* put chip into reset */ + val = *(idt->reg_gp); /* wait... */ + val |= *(idt->reg_gp); /* wait... */ + val |= *(idt->reg_gp); /* wait... */ + *(idt->reg_cfg) = 0; /* out of reset */ + + splx(s); + + return; +} + +/****************************************************************************** + * + * Initialize the hardware + */ + +void +phys_init(nicstar_reg_t * const idt) +{ + int i; + u_long t; + +#ifdef NICSTAR_TESTSRAM + u_long z, s2, bad; +#endif + u_long x, s1; + volatile u_long *regCFG = (volatile u_long *)(idt->virt_baseaddr + REGCFG); + volatile u_long *regGP = (volatile u_long *)(idt->virt_baseaddr + REGGP); + volatile u_long stat_val; + + /* clean status bits */ + stat_val = *(volatile u_long *)idt->stat_reg; + *(volatile u_long *)idt->stat_reg = stat_val | 0xcc30; /* clear ints */ + + idt->flg_le25 = 0; /* is this FORE LE25 with 77105 PHY? */ + idt->flg_igcrc = 0; /* ignore receive CRC errors? */ + idt->hardware = "?"; + + /* start signalling SAR reset */ + *regCFG = 0x80000000; + + /* SAR reset--clear occurs at lease 2 PCI cycles after setting */ + t = *regGP; /* wait */ + t = *regCFG; + *regCFG = 0; /* clear reset */ + + *regGP = 0x00000000; /* clear PHYS reset */ + *regGP = 0x00000008; /* start PHYS reset */ + t = *regGP; /* wait */ + t = *regCFG; + *regGP = 0x00000001; /* set while changing SUNI settings */ + t = *regGP; /* wait */ + t = *regCFG; + + idt->flg_le25 = idt_phys_detect(idt); + + if (idt->flg_le25) { + idt->cellrate_rmax = 59259; + idt->cellrate_tmax = 59259; + idt->cellrate_rcur = 0; + idt->cellrate_tcur = 0; + idt->txslots_max = 348; /* use n*348 for higher resolution */ + idt->txslots_cur = 0; + nicstar_util_wr(idt, 0, 0x00, 0x00); /* synch (needed for + * 77105?) */ + nicstar_util_wr(idt, 1, 0x00, 0x09); /* enable interrupts */ + nicstar_util_wr(idt, 1, 0x02, 0x10); /* 77105 RFLUSH */ + nicstar_util_rd(idt, 0x01, &t); /* read/clear interrupt flag */ + } else { + idt->cellrate_rmax = 353207; /* 2075 slots of 1 DS0 each... */ + idt->cellrate_tmax = 353207; + idt->cellrate_rcur = 0; + idt->cellrate_tcur = 0; + idt->txslots_max = 2075; + idt->txslots_cur = 0; + + /* initialize the 155Mb SUNI */ + nicstar_util_wr(idt, 0, 0x00, 0x00); /* sync utopia with SAR */ + nicstar_util_wr(idt, 1, 0x00, 0x00); /* clear SW reset */ + *regGP = 0x00000000; /* clear when done with SUNI changes */ + } + +#ifdef NICSTAR_TESTSRAM + /* + * this will work with 32K and 128K word RAM because the pattern + * repeats every 4 words + */ + for (i = 0; i < 0x20000; i += 4) + (void)nicstar_sram_wr(idt, i, 4, 0xa5a5a5a5, 0x5a5a5a5a, + 0xa5a5a5a5, 0x5a5a5a5a); + for (i = 0; i < 0x20000; i += 2) { + s1 = nicstar_sram_rd(idt, i, &x); + s2 = nicstar_sram_rd(idt, i + 1, &z); + if (s1 || s2 || x != 0xa5a5a5a5 || z != 0x5a5a5a5a) { + printf("sram fail1 %d 0x%08x 0x%08x\n", i, x, z); + break; + } + } + for (i = 0; i < 0x20000; i += 4) + (void)nicstar_sram_wr(idt, i, 4, 0x5a5a5a5a, 0xa5a5a5a5, + 0x5a5a5a5a, 0xa5a5a5a5); + for (i = 0; i < 0x20000; i += 2) { + s1 = nicstar_sram_rd(idt, i, &z); + s2 = nicstar_sram_rd(idt, i + 1, &x); + if (s1 || s2 || x != 0xa5a5a5a5 || z != 0x5a5a5a5a) { + printf("sram fail2 %d 0x%08x 0x%08x\n", i, x, z); + break; + } + } +#endif + + /* flush SRAM */ + for (i = 0; i < 0x20000; i += 4) + (void)nicstar_sram_wr(idt, i, 4, 0, 0, 0, 0); + + /* + * the memory map for the 32K word card has the + * addresses 0x8000, 0x10000, 0x18000 mapped back + * to address 0, and 0x8001, ..., 0x18001 is mapped + * to address 1. address 0x4000 is mapped to 0x1c000 + */ + + /* write in the 0 word, see if we read it at 0x10000 */ + (void)nicstar_sram_wr(idt, 0x0, 1, 0xa5a5a5a5, 0, 0, 0); + s1 = nicstar_sram_rd(idt, 0x10000, &x); + (void)nicstar_sram_wr(idt, 0x0, 1, 0, 0, 0, 0); + if (!s1 && x == 0xa5a5a5a5) { + device_printf(idt->dev, "32K words of RAM\n"); + idt->sram = 0x4000; + } else { + device_printf(idt->dev, "128K words of RAM\n"); + idt->sram = 0x10000; + } +#ifdef NICSTAR_FORCE32K + idt->sram = 0x4000; + device_printf(idt->dev, "forced to 32K words of RAM\n"); +#endif + + return; +} + +/* Cellrate notes: + * The cellrate for OC3 is 353207.55, rounded down above. This makes + * 2075 slots of one DS0 (64003) each. + * + * The ATM25 rate is calculated from 25.6mb divided by 424 bits for + * cell plus 8 bits for "opcode" == 432 bits. 59259 * 432 = 25599888. + * This provides a 47-byte AAL1 bitrate of 22,281,384 bits/sec, or + * 348 slots of one DS0 (64027) each. If 8khz synch events are to + * be sent, then only 347 slots are available. + * + ****************************************************************************** + * + * Physical layer detect + * + * in: IDT device + * out: zero = LE155, NZ = LE25 + * + * Date first: 10/30/2000 last: 06/08/2001 + */ + +int +idt_phys_detect(IDT * idt) +{ + u_long t; + int retval; + + retval = 0; + + nicstar_util_wr(idt, 0, 0x00, 0x00); /* synch (needed for 77105?) */ + nicstar_util_rd(idt, 0x00, &t); /* get Master Control Register */ + + switch (t) { + /* 25.6 Mbps ATM PHY with TC & PMD */ + /* http://www.idt.com/products/pages/ATM_Products-77105.html */ + case 0x09: + device_printf(idt->dev, "ATM card is Fore LE25, PHY=77105\n"); + idt->hardware = "ATM25/77105"; + retval = 1; + break; + + /* S/UNI-155-LITE */ + /* http://www.pmc-sierra.com/products/details/pm5346/index.html */ + case 0x30: + device_printf(idt->dev, "ATM card is Fore LE155 or IDT, PHY=PM5346\n"); + idt->hardware = "ATM155/PM5346"; + break; + + /* S/UNI-155-ULTRA */ + /* http://www.pmc-sierra.com/products/details/pm5350/index.html */ + case 0x31: + case 0x70: + case 0x78: + device_printf(idt->dev, "ATM card is Fore LE155, PHY=PM5350\n"); + idt->hardware = "ATM155/PM5350"; + break; + + default: + device_printf(idt->dev, + "cannot figure out card type, assuming LE155 (reg=%d).\n", + (int)t); + idt->hardware = "unknown (LE155?)"; + break; + } + return (retval); +} + +/* Register 0 values: + * 77105 = 0x09 + * PM5346 = 0x30 + * PM5250 = 0x31 (actually observed) + * PM5350 = 0x70 or 0x78 (according to docs) + * + ****************************************************************************** + * + * Initialize the data structures + */ + +void +nicstar_init(nicstar_reg_t * const idt) +{ + int i; + vm_offset_t buf; + u_long *p; + + idt_connect_init(idt, 0); /* initialize for 0 VPI bits (12 VCI + * bits) */ + + /* allocate space for TSQ, RSQ, SCD for VBR,ABR, UBR */ + idt->fixbuf = vm_page_alloc_contig(NICSTAR_FIXPAGES * PAGE_SIZE, + 0x100000, 0xffffffff, 0x2000); + if (idt->fixbuf == NULL) + return; /* no space card disabled */ + + if (idt_buffer_init(idt)) /* allocate large buffers */ + goto freemem; /* free memory and return */ + + if (idt_mcheck_init(idt)) + goto freemem; + + idt_found++; /* number of cards found on machine */ + + if (bootverbose) { + printf("nicstar: buffer size %d\n", 0); + } + idt_queue_init(idt); /* initialize all TX_QUEUE structures */ + idt_slots_init(idt); /* initialize CBR table slots */ + + /* initialize variable rate mbuf queues */ + + bzero((caddr_t)idt->fixbuf, NICSTAR_FIXPAGES * PAGE_SIZE); + + /* TSQ initialization */ + for (p = (u_long *)idt->fixbuf; p < (u_long *)(idt->fixbuf + 0x2000);) { + *p++ = 0x00000000; + *p++ = 0x80000000; /* set empty bit */ + } + + buf = vtophys(idt->fixbuf); + /* Transmit Status Queue Base */ + *(volatile u_long *)(idt->virt_baseaddr + REGTSQB) = buf; + /* Transmit Status Queue Head */ + *(volatile u_long *)(idt->virt_baseaddr + REGTSQH) = 0; /* 8k aligned */ + idt->tsq_base = (u_long *)idt->fixbuf; + idt->tsq_head = (u_long *)idt->fixbuf; + idt->tsq_size = 1024; + + /* Recieve Status Queue Base */ + *(volatile u_long *)(idt->virt_baseaddr + REGRSQB) = buf + 0x2000; + /* Transmit Status Queue Head */ + *(volatile u_long *)(idt->virt_baseaddr + REGRSQH) = 0; /* 8k aligned */ + idt->rsqh = 0; + + + /* Now load receive buffers into SRAM */ + nicstar_ld_rcv_buf(idt); + + /* load variable SCQ */ + (void)nicstar_sram_wr(idt, 0x1e7dc, 4, (u_long)(buf + 0x8000), 0, + 0xffffffff, 0); /* SD2 */ + (void)nicstar_sram_wr(idt, 0x1e7e0, 4, 0, 0, 0, 0); + (void)nicstar_sram_wr(idt, 0x1e7e4, 4, 0, 0, 0, 0); + + (void)nicstar_sram_wr(idt, 0x1e7e8, 4, (u_long)(buf + 0x6000), 0, + 0xffffffff, 0); /* SD1 */ + (void)nicstar_sram_wr(idt, 0x1e7ec, 4, 0, 0, 0, 0); + (void)nicstar_sram_wr(idt, 0x1e7f0, 4, 0, 0, 0, 0); + + (void)nicstar_sram_wr(idt, 0x1e7f4, 4, (u_long)(buf + 0x4000), 0, + 0xffffffff, 0); /* SD0 */ + (void)nicstar_sram_wr(idt, 0x1e7f8, 4, 0, 0, 0, 0); + (void)nicstar_sram_wr(idt, 0x1e7fc, 4, 0, 0, 0, 0); + + /* initialize RCT */ + for (i = 0; i < idt->sram; i += 4) { /* XXX ifdef table size */ + nicstar_sram_wr(idt, i, 4, 0x0, 0x0, 0x0, 0xffffffff); + } + + /* VPI/VCI mask is 0 */ + *(volatile u_long *)(idt->virt_baseaddr + REGVMSK) = 0; + + /* Set the Transmit Schedule Table base address */ + *(volatile u_long *)(idt->virt_baseaddr + REGTSTB) = IDT_TST_START; + + +/* Configuration Register settings: + * Bit(s) Meaning value + * 31 Software reset 0 + * 30 RESERVED 0 + * 29 Recieve Enabled 1 + * 28-27 Small Buffer Size (host memory) 01 (96 bytes) + * 26-25 Large Buffer Size (host memory) 00 (2048 bytes) + * 24 Interrupt on empty free buffer queue 1 + * + * 23-22 Recieve Status Queue Size (host memory) 10 (8192 bytes) + * 21 Accpect Invalid cells into Raw Queue 1 + * 20 Ignore General Flow control 1 + * + * 19-18 VPI/VCI Select 00 + * 17-16 Recieve Connect Table Size 00 (32K SRAM) + * 10 (128K SRAM) + * + * 15 Accpect non-open VPI/VCI to Raw Queue 1 + * 14-12 time to delay after Rx and interrupt 001 (0us) + * + * 11 Interrupt when a Raw Cell is added 1 + * 10 Interrupt when Recieve Queue near full 1 + * 9 Recieve RM (PTI = 110 or 111) 1 + * 8 RESERVED 0 + * + * 7 Interrupt on Timer rollover 1 + * 6 RESERVED 0 + * 5 Transmit Enabled 1 + * 4 Interrupt on Transmit Status Indicator 1 + * + * 3 Interrupt on transmit underruns 1 + * 2 UTOPIA cell/byte mode 0 (cell) + * 1 Interrupt on nearly full TSQ 1 + * 0 Enable Physical Interrupt 1 + */ + + /* original values: 0x31b09ebb and 0x31b29eb */ + /* + * 11/01/2000: changed from 0x31b09eb to 0x29b09eb for 96-byte + * sm-buf + */ + + if (idt->sram == 0x4000)/* 32K */ + *(volatile u_long *)(idt->virt_baseaddr + REGCFG) = 0x29b09ebb; + else /* 128K */ + *(volatile u_long *)(idt->virt_baseaddr + REGCFG) = 0x29b29ebb; + + return; + +freemem: + /* free memory and return */ + idt_release_mem(idt); + device_printf(idt->dev, "cannot allocate memory\n"); + return; /* no space card disabled */ +} + +/****************************************************************************** + * + * Release all allocated memory + * + * in: IDT device + * + * Date first: 11/14/2000 last: 11/14/2000 + */ + +void +idt_release_mem(IDT * idt) +{ + if (idt->fixbuf != NULL) + kmem_free(kernel_map, idt->fixbuf, + (NICSTAR_FIXPAGES * PAGE_SIZE)); + + if (idt->cbr_base != NULL) + kmem_free(kernel_map, (vm_offset_t)idt->cbr_base, idt->cbr_size); + + printf("%s() is NOT SAFE!\n", __func__); + + /* we also have idt->connection and idt->mcheck to do as well... */ +} + +/****************************************************************************** + * + * Write one to four words to SRAM + * + * writes one to four words into sram starting at "sram_location" + * + * returns -1 if sram location is out of range. + * returns count, if count is not in the range from 1-4. + * returns 0 if parameters were acceptable + */ + +static int +nicstar_sram_wr(nicstar_reg_t * const idt, u_long address, int count, + u_long data0, u_long data1, u_long data2, u_long data3) +{ + if (address >= 0x20000) /* bad address */ + return (-1); + + if (idt_status_wait(idt)) /* 12/06/2000 */ + return (-1); + + switch (--count) { + case 3: + *(idt->reg_data + 3) = data3; /* drop down to do others */ + case 2: + *(idt->reg_data + 2) = data2; /* drop down to do others */ + case 1: + *(idt->reg_data + 1) = data1; /* drop down to do others */ + case 0: + *idt->reg_data = data0; /* load last data item */ + break; /* done loading values */ + default: + return (count); /* nothing to do */ + } + /* write the word(s) */ + *idt->reg_cmd = 0x40000000 | (address << 2) | count; + + return (0); +} + +/* 05/31/2001: Removed wait between data register(s) and write command. + * The docs do not state it is helpful, and the example only has one + * wait, before the data register load. The wait time is very high - + * aproximately 6 microseconds per wait. + * + ****************************************************************************** + * + * Read one word from SRAM + * + * reads one word of sram at "sram_location" and places the value + * in "answer_pointer" + * + * returns -1 if sram location is out of range. + * returns 0 if parameters were acceptable + */ +static int +nicstar_sram_rd(nicstar_reg_t * const idt, u_long address, u_long *data0) +{ + if (address >= 0x20000) /* bad address */ + return (-1); + + if (idt_status_wait(idt)) + return (-1); + + *idt->reg_cmd = 0x50000000 | (address << 2); /* read a word */ + + if (idt_status_wait(idt)) + return (-1); + + *data0 = *idt->reg_data;/* save word */ + + return (0); +} + +/******************************************************************************* + * + * Open or Close connection in IDT Receive Connection Table + * + * in: IDT device, VPI, VCI, opflag (0 = close, 1 = open) + * out: zero = success + * + * Date first: 12/14/2000 last: 12/14/2000 + */ + +int +idt_connect_opencls(IDT * idt, CONNECTION * connection, int opflag) +{ + int address; + int word1; + + if (connection->vpi >= idt->conn_maxvpi || + connection->vci >= idt->conn_maxvci) + return (1); + + address = connection->vpi * idt->conn_maxvci + connection->vci; + address <<= 2; /* each entry is 4 words */ + + if (opflag) { + switch (connection->aal) { + case ATM_AAL0: + word1 = 0x00038000; + break; /* raw cell queue */ + case ATM_AAL1: + word1 = 0x00008000; + break; /* Nicstar "AAL0" */ + case ATM_AAL3_4: + word1 = 0x00018000; + break; + case ATM_AAL5: + word1 = 0x00028000; + break; + default: + return (1); + } + nicstar_sram_wr(idt, address, 4, word1, 0, 0, 0xffffffff); + opflag = 0x00080000; /* bit-19 set or clear */ + } + if (idt_status_wait(idt)) + return (1); + + *idt->reg_cmd = 0x20000000 | opflag | address << 2; + return (0); +} + +/******************************************************************************* + * + * nicstar_add_buf ( card, mbuf1, mbuf2, which_queue) + * + * This adds two buffers to the specified queue. This uses the + * mbuf address as handle and the buffer physical address must be + * the DMA address. + * + * returns -1 if queue is full, the address is not word aligned, or + * a invalid queue is specified + * returns 0 if parameters were acceptable + */ + +int +nicstar_add_buf(nicstar_reg_t * const idt, struct mbuf * buf0, + struct mbuf * buf1, u_long islrg) +{ + u_long stat_val; + u_long val0, val1, val2, val3; + + if (islrg > 1) /* bad buffer size */ + return (-1); + + stat_val = *idt->reg_stat; + + if (islrg) { + if (stat_val & 0x80) /* large queue is full */ + return (-1); + } else if (stat_val & 0x100) /* small queue is full */ + return (-1); + + if (!buf0 || !buf1 || ((u_long)(buf0->m_data) & 0x7) + || ((u_long)(buf1->m_data) & 0x7)) { + return (-1); /* buffers must word aligned */ + } + if (idt->raw_headm == NULL) /* raw cell buffer pointer not + * initialized */ + if (islrg) { + idt->raw_headm = buf0; + idt->raw_headp = vtophys(buf0->m_data); + } + if (idt_status_wait(idt)) /* 12/06/2000 */ + return (-1); + + val0 = (u_long)buf0; /* mbuf address is handle */ + val1 = vtophys(buf0->m_data); /* DMA addr of buff1 */ + val2 = (u_long)buf1; /* mbuf address is handle */ + val3 = vtophys(buf1->m_data); /* DMA addr of buff2 */ + + *(idt->reg_data + 0) = val0; + *(idt->reg_data + 1) = val1; + *(idt->reg_data + 2) = val2; + *(idt->reg_data + 3) = val3; + + *idt->reg_cmd = 0x60000000 | islrg; + + idt_mcheck_add(idt, buf0); + idt_mcheck_add(idt, buf1); + + return (0); +} + +/****************************************************************************** + * + * nicstar_util_rd ( card, util_location, answer_pointer ) + * + * reads one byte from the utility bus at "util_location" and places the + * value in "answer_pointer" + * + * returns -1 if util location is out of range. + * returns 0 if parameters were acceptable + */ +static int +nicstar_util_rd(nicstar_reg_t * const idt, u_long address, u_long *data) +{ + + if (address >= 0x81) /* bad address */ + return (-1); + + if (idt_status_wait(idt)) + return (-1); + + *idt->reg_cmd = 0x80000200 | address; /* read a word */ + + if (idt_status_wait(idt)) + return (-1); + + *data = *idt->reg_data & 0xff; /* save word */ + + return (0); +} + +/****************************************************************************** + * + * nicstar_util_wr ( card, util location, data ) + * + * writes one byte to the utility bus at "util_location" + * + * returns -1 if util location is out of range. + * returns 0 if parameters were acceptable + */ +static int +nicstar_util_wr(nicstar_reg_t * const idt, int cs, u_long address, u_long data) +{ + + if (address >= 0x81) /* bad address */ + return (-1); + if (cs > 1) + return (-1); + + if (idt_status_wait(idt)) + return (-1); + + *idt->reg_data = data & 0xff; /* load last data item */ + + if (cs == 0) + *idt->reg_cmd = 0x90000100 | address; /* write the byte, CS1 */ + else + *idt->reg_cmd = 0x90000200 | address; /* write the byte, CS2 */ + + return (0); +} + +/****************************************************************************** + * + * nicstar_eeprom_rd ( card , byte_location ) + * + * reads one byte from the utility bus at "byte_location" and return the + * value as an integer. this routint is only used to read the MAC address + * from the EEPROM at boot time. + */ +int +nicstar_eeprom_rd(nicstar_reg_t * const idt, u_long address) +{ + volatile u_long *regGP = (volatile u_long *)(idt->virt_baseaddr + REGGP); + volatile u_long gp = *regGP & 0xfffffff0; + int i, value = 0; + + DELAY(5); /* make sure idle */ + *regGP = gp | 0x06; /* CS and Clock high */ + DELAY(5); + *regGP = gp; /* CS and Clock low */ + DELAY(5); + /* toggle in READ CMD (00000011) */ + *regGP = gp | 0x04; /* Clock high (data 0) */ + DELAY(5); + *regGP = gp; /* CS and Clock low */ + DELAY(5); + *regGP = gp | 0x04; /* Clock high (data 0) */ + DELAY(5); + *regGP = gp; /* CS and Clock low */ + DELAY(5); + *regGP = gp | 0x04; /* Clock high (data 0) */ + DELAY(5); + *regGP = gp; /* CS and Clock low */ + DELAY(5); + *regGP = gp | 0x04; /* Clock high (data 0) */ + DELAY(5); + *regGP = gp; /* CS and Clock low */ + DELAY(5); + *regGP = gp | 0x04; /* Clock high (data 0) */ + DELAY(5); + *regGP = gp; /* CS and Clock low */ + DELAY(5); + *regGP = gp | 0x04; /* Clock high (data 0) */ + DELAY(5); + *regGP = gp | 0x01; /* CS and Clock low data 1 */ + DELAY(5); + *regGP = gp | 0x05; /* Clock high (data 1) */ + DELAY(5); + *regGP = gp | 0x01; /* CS and Clock low data 1 */ + DELAY(5); + *regGP = gp | 0x05; /* Clock high (data 1) */ + DELAY(5); + /* toggle in the address */ + for (i = 7; i >= 0; i--) { + *regGP = (gp | ((address >> i) & 1)); /* Clock low */ + DELAY(5); + *regGP = (gp | 0x04 | ((address >> i) & 1)); /* Clock high */ + DELAY(5); + } + /* read EEPROM data */ + for (i = 7; i >= 0; i--) { + *regGP = gp; /* Clock low */ + DELAY(5); + value |= ((*regGP & 0x10000) >> (16 - i)); + *regGP = gp | 0x04; /* Clock high */ + DELAY(5); + } + *regGP = gp; /* CS and Clock low */ + return (value); +} + +/******************************************************************************* + * + * Load the card receive buffers + * + * in: IDT device + * + * Date first: 11/01/2000 last: 05/25/2000 + */ + +void +nicstar_ld_rcv_buf(IDT * idt) +{ + struct mbuf *m1, *m2; + u_long stat_reg; + int card_small; + int card_large; + int s; + + s = splimp(); + + stat_reg = *(volatile u_long *)idt->stat_reg; + + card_small = (stat_reg & 0xff000000) >> 23; /* reg is number of + * pairs */ + card_large = (stat_reg & 0x00ff0000) >> 15; + + if (idt_sysctl_bufsmall > 510) + idt_sysctl_bufsmall = 510; + if (idt_sysctl_buflarge > 510) + idt_sysctl_buflarge = 510; + if (idt_sysctl_bufsmall < 10) + idt_sysctl_bufsmall = 10; + if (idt_sysctl_buflarge < 10) + idt_sysctl_buflarge = 10; + + while (card_small < idt_sysctl_bufsmall) { /* 05/25/2001 from fixed */ + MGETHDR(m1, M_DONTWAIT, MT_DATA); + if (m1 == NULL) + break; + MGETHDR(m2, M_DONTWAIT, MT_DATA); + if (m2 == NULL) { + m_free(m1); + break; + } + MH_ALIGN(m1, 96); /* word align & allow lots of + * prepending */ + MH_ALIGN(m2, 96); + if (nicstar_add_buf(idt, m1, m2, 0)) { + device_printf(idt->dev, + "Cannot add small buffers, size=%d.\n", + card_small); + m_free(m1); + m_free(m2); + break; + } + card_small += 2; + } + + while (card_large < idt_sysctl_buflarge) { /* 05/25/2001 from fixed */ + m1 = idt_mbufcl_get(); + if (m1 == NULL) + break; + m2 = idt_mbufcl_get(); + if (m2 == NULL) { + m_free(m1); + break; + } + if (nicstar_add_buf(idt, m1, m2, 1)) { + device_printf(idt->dev, + "Cannot add large buffers, size=%d.\n", + card_large); + m_free(m1); + m_free(m2); + break; + } + card_large += 2; + } + idt_sysctl_curlarge = card_large; + idt_sysctl_cursmall = card_small; + + splx(s); +} + +/******************************************************************************* + * + * Wait for command to finish + * + * in: IDT device + * out: zero = success + * + * Date first: 12/06/2000 last: 12/16/2000 + */ + +int +idt_status_wait(IDT * idt) +{ + int timeout; + + timeout = 33 * 100; /* allow 100 microseconds timeout */ + + while (*idt->reg_stat & 0x200) + if (--timeout == 0) { + device_printf(idt->dev, + "timeout waiting for device status.\n"); + idt->stats_cmderrors++; + return (1); + } + return (0); +} + +/******************************************************************************* + * + * Log status of system buffers + * + * in: IDT device + * + * Date first: 10/31/2000 last: 05/25/2001 + */ + +void +idt_status_bufs(IDT * idt) +{ + u_long stat_reg; + int card_small; + int card_large; + int s; + + s = splimp(); + + stat_reg = *(volatile u_long *)idt->stat_reg; + + card_small = (stat_reg & 0xff000000) >> 23; /* reg is number of + * pairs */ + card_large = (stat_reg & 0x00ff0000) >> 15; + + splx(s); + + device_printf(idt->dev, "BUFFER STATUS: small=%d/%d, large=%d/%d.\n", + card_small, idt_sysctl_bufsmall, + card_large, idt_sysctl_buflarge); +} + +/* Since this is called when the card timer wraps, we should only see + * this 16 times (LE155) or 10 (LE25) per hour. + * + ******************************************************************************* + * + * Add mbuf into "owned" list + * + * in: IDT device, mbuf + * out: zero = success + * + * Date first: 11/13/2000 last: 11/13/2000 + */ + +int +idt_mcheck_add(IDT * idt, struct mbuf * m) +{ + int hpos; + int s; + + hpos = (((int)m) >> 8) & 1023; + s = splimp(); + + m->m_next = idt->mcheck[hpos]; + idt->mcheck[hpos] = m; + + splx(s); + return (0); +} + +/****************************************************************************** + * + * Remove mbuf from "owned" list + * + * in: IDT device, mbuf + * out: zero = success + * + * Date first: 11/13/2000 last: 11/13/2000 + */ + +int +idt_mcheck_rem(IDT * idt, struct mbuf * m) +{ + struct mbuf *nbuf; + int hpos; + int s; + + hpos = (((int)m) >> 8) & 1023; + s = splimp(); + + nbuf = idt->mcheck[hpos]; + + if (nbuf == m) { + idt->mcheck[hpos] = m->m_next; + splx(s); + m->m_next = NULL; + return (0); + } + while (nbuf != NULL) { + if (nbuf->m_next != m) { + nbuf = nbuf->m_next; + continue; + } + nbuf->m_next = m->m_next; + splx(s); + m->m_next = NULL; + return (0); + } + + splx(s); + device_printf(idt->dev, "Card should not have this mbuf! %x\n", (int)m); + return (1); +} + +/****************************************************************************** + * + * Initialize mbuf "owned" list + * + * in: IDT device + * out: zero = success + * + * Date first: 11/13/2000 last: 05/26/2001 + */ + +int +idt_mcheck_init(IDT * idt) +{ + int size; + int x; + + size = round_page(sizeof(struct mbuf *) * 1024); + idt->mcheck = (struct mbuf **) vm_page_alloc_contig(size, + 0x100000, 0xffffffff, 0x2000); + if (idt->mcheck == NULL) + return (1); + + for (x = 0; x < 1024; x++) + idt->mcheck[x] = NULL; + + return (0); +} + +/****************************************************************************** + * + * Allocate contiguous, fixed memory + * + * in: number of pages + * out: pointer, NULL = failure + * + * Date first: 11/29/2000 last: 11/29/2000 + */ + +vm_offset_t +idt_malloc_contig(int pages) +{ + vm_offset_t retval; + + retval = vm_page_alloc_contig(pages * PAGE_SIZE, + 0x100000, 0xffffffff, 0x2000); +#ifdef UNDEF + printf("idt: vm_offset_t allocated %d pages at %x\n", pages, retval); +#endif + + return (retval); +} + +/******************************************************************************* + * + * Initialize all TX_QUEUE structures + * + * in: IDT device + * out: zero = succes + * + * Date first: 11/29/2000 last: 11/29/2000 + */ +static int +idt_queue_init(IDT * idt) +{ + TX_QUEUE *txqueue; + vm_offset_t scqbase; + int x; + + idt->cbr_size = IDT_MAX_CBRQUEUE * 16 * 64; + idt->cbr_base = idt_malloc_contig(idt->cbr_size / PAGE_SIZE); + scqbase = idt->cbr_base; + if (scqbase == NULL) + return (1); + idt->cbr_freect = idt->cbr_size / (16 * 64); + + for (x = 0; x < idt->cbr_freect; x++) { + txqueue = &idt->cbr_txqb[x]; + txqueue->mget = NULL; + txqueue->mput = NULL; + txqueue->scd = IDT_SCD_START + x * 12; + txqueue->scq_base = (u_long *)scqbase; + txqueue->scq_next = txqueue->scq_base; + txqueue->scq_last = txqueue->scq_next; + txqueue->scq_len = 64; /* all CBR queues use 64 entries */ + txqueue->scq_cur = 0; + txqueue->rate = 0; + txqueue->vbr_m = 0; /* m & n set to zero for CBR */ + txqueue->vbr_n = 0; + idt->cbr_free[x] = txqueue; + scqbase += 64 * 16; + nicstar_sram_wr(idt, txqueue->scd, 4, + vtophys(txqueue->scq_base), 0, 0xffffffff, 0); + } + + txqueue = &idt->queue_vbr; /* VBR queue */ + txqueue->mget = NULL; + txqueue->mput = NULL; + txqueue->scd = 0x1e7f4; + txqueue->scq_base = (u_long *)(idt->fixbuf + 0x4000); + txqueue->scq_next = txqueue->scq_base; + txqueue->scq_last = txqueue->scq_next; + txqueue->scq_len = 512; /* all VBR queues use 512 entries */ + txqueue->scq_cur = 0; + txqueue->rate = 0; + txqueue->vbr_m = 1; + txqueue->vbr_n = 1; + nicstar_sram_wr(idt, txqueue->scd, 4, + vtophys(txqueue->scq_base), 0, 0xffffffff, 0); + + txqueue = &idt->queue_abr; /* ABR queue (not currently used) */ + txqueue->mget = NULL; + txqueue->mput = NULL; + txqueue->scd = 0x1e7e8; + txqueue->scq_base = (u_long *)(idt->fixbuf + 0x6000); + txqueue->scq_next = txqueue->scq_base; + txqueue->scq_last = txqueue->scq_next; + txqueue->scq_len = 512; + txqueue->scq_cur = 0; + txqueue->rate = 0; + txqueue->vbr_m = 1; + txqueue->vbr_n = 1; + nicstar_sram_wr(idt, txqueue->scd, 4, + vtophys(txqueue->scq_base), 0, 0xffffffff, 0); + + txqueue = &idt->queue_ubr; /* UBR queue */ + txqueue->mget = NULL; + txqueue->mput = NULL; + txqueue->scd = 0x1e7dc; + txqueue->scq_base = (u_long *)(idt->fixbuf + 0x8000); + txqueue->scq_next = txqueue->scq_base; + txqueue->scq_last = txqueue->scq_next; + txqueue->scq_len = 512; + txqueue->scq_cur = 0; + txqueue->rate = 0; + txqueue->vbr_m = 1; /* since the ABR queue is lowest priority, */ + txqueue->vbr_n = 1; /* these factors should never change */ + nicstar_sram_wr(idt, txqueue->scd, 4, + vtophys(txqueue->scq_base), 0, 0xffffffff, 0); + + return (0); +} + +/******************************************************************************* + * + * Get mbuf chain from TX_QUEUE + * + * in: CONNECTION + * out: mbuf, NULL = empty + * + * Date first: 12/03/2000 last: 12/03/2000 + */ +static struct mbuf * +idt_queue_get(TX_QUEUE * txqueue) +{ + struct mbuf *m1, *m2; + int s; + + if (txqueue == NULL) + return (NULL); + + s = splimp(); + + m1 = txqueue->mget; + if (m1 != NULL) { + m2 = m1->m_nextpkt; + txqueue->mget = m2; + if (m2 == NULL) /* is queue empty now? */ + txqueue->mput = NULL; + } + splx(s); + + return (m1); +} + +/******************************************************************************* + * + * Add mbuf chain to connection TX_QUEUE + * + * in: CONNECTION, mbuf chain + * out: zero = succes + * + * Date first: 12/03/2000 last: 06/01/2001 + */ +static int +idt_queue_put(CONNECTION * connection, struct mbuf * m) +{ + TX_QUEUE *txqueue; + int s; + + if (connection == NULL) { + m_freem(m); + return (1); + } + txqueue = connection->queue; + if (txqueue == NULL) { + m_freem(m); + return (1); + } + m->m_nextpkt = NULL; + m->m_pkthdr.rcvif = (struct ifnet *) connection; + + s = splimp(); + + if (txqueue->mput != NULL) { + *txqueue->mput = m; + txqueue->mput = &m->m_nextpkt; + } else { /* queue is empty */ + txqueue->mget = m; + txqueue->mput = &m->m_nextpkt; + } + splx(s); + + return (0); +} + +/******************************************************************************* + * + * Flush all connection mbufs from TX_QUEUE + * + * in: CONNECTION + * out: zero = succes + * + * Date first: 12/03/2000 last: 12/03/2000 + */ +static int +idt_queue_flush(CONNECTION * connection) +{ + TX_QUEUE *txqueue; + struct mbuf **m0, *m1; + int s; + + if (connection == NULL) + return (1); + txqueue = connection->queue; + if (txqueue == NULL) + return (1); + + s = splimp(); + + m0 = &txqueue->mget; + m1 = *m0; + while (m1 != NULL) { + if (m1->m_pkthdr.rcvif == (struct ifnet *) connection) { + *m0 = m1->m_nextpkt; + m_freem(m1); + m1 = *m0; + continue; + } + m0 = &m1->m_nextpkt; + m1 = *m0; + } + txqueue->mput = m0; + splx(s); + + return (0); +} + +/******************************************************************************* + * + * Calculate number of table positions for CBR connection + * + * in: IDT device, PCR (cells/second) + * out: table positions needed (minimum = 1) + * + * Date first: 11/29/2000 last: 06/12/2001 + */ +int +idt_slots_cbr(IDT * idt, int pcr) +{ + unsigned int bitrate; + unsigned int slots; + unsigned int rem; + + if (pcr == 171) { + if (idt_sysctl_logvcs) + device_printf(idt->dev, + "idt_slots_cbr: CBR channel=64000, 1 slot\n"); + return (1); + } + if (pcr < 171) { + if (idt_sysctl_logvcs) + device_printf(idt->dev, + "idt_slots_cbr: CBR pcr %d rounded up to 1 slot\n", pcr); + return (1); + } + bitrate = pcr * 47 * 8; + slots = bitrate / 64000; + rem = bitrate % 64000; + if (rem && idt_sysctl_logvcs) + device_printf(idt->dev, + "idt_slots_cbr: CBR cell rate rounded down to %d from %d\n", + ((slots * 64000) / 376), pcr); /* slots++; */ + + if (idt_sysctl_logvcs) + device_printf(idt->dev, + "idt_slots_cbr: CBR pcr=%d, slots=%d.\n", pcr, slots); + return (slots); +} + +/* The original algorithm rounded up or down by 32k, the goal being to + * map 64000 requests exactly. Unfortunately, this caused one particular + * SVC to be set one slot too low, causing mbuf cluster starvation. + * We can still handle the single 64k channel with a special case, and + * let all others fall where they may. + * + ******************************************************************************* + * + * Add TX QUEUE pointer to slots in CBR table + * + * in: IDT device, TX_QUEUE, number slots + * out: zero = success + * + * Date first: 11/29/2000 last: 06/11/2001 + */ +static int +idt_slots_add(IDT * idt, TX_QUEUE * queue, int slots) +{ + TX_QUEUE *curval; + int p_max; /* extra precision slots maximum */ + int p_spc; /* extra precision spacing value */ + int p_ptr; /* extra precision pointer */ + int qptr, qmax; + int qlast; + int scdval; + + if (slots < 1) + return (1); + + qmax = idt->txslots_max; + p_max = qmax << 8; + p_spc = p_max / slots; + p_ptr = p_spc >> 1; /* use half spacing for start point */ + qptr = p_ptr >> 8; + qlast = qptr; + + scdval = 0x20000000 | queue->scd; + + if (CBR_VERBOSE) { + printf("idt_slots_add: p_max = %d\n", p_max); + printf("idt_slots_add: p_spc = %d\n", p_spc); + printf("idt_slots_add: p_ptr = %d\n", p_ptr); + printf("idt_slots_add: qptr = %d\n", qptr); + } + while (slots) { + if (qptr >= qmax) /* handle wrap for empty slot choosing */ + qptr -= qmax; + curval = idt->cbr_slot[qptr]; + if (curval != NULL) { /* this slot has CBR, so try next */ + qptr++; /* next slot */ + continue; + } + if (CBR_VERBOSE) { + printf("idt_slots_add: using qptr %d (%d)\n", qptr, qptr - qlast); + qlast = qptr; + } + idt->cbr_slot[qptr] = queue; + nicstar_sram_wr(idt, qptr + IDT_TST_START, 1, scdval, 0, 0, 0); + slots--; + p_ptr += p_spc; + if (p_ptr >= p_max) /* main pointer wrap */ + p_ptr -= p_max; + qptr = p_ptr >> 8; + } + return (0); +} + +/* 06/11/2001: Extra precision pointer is used in order to handle cases where + * fractional slot spacing causes a large area of slots to be filled. + * This can cause further CBR circuits to get slots that have very + * poor spacing. + * + ******************************************************************************* + * + * Remove TX QUEUE pointer from slots in CBR table + * + * in: IDT device, TX_QUEUE + * out: number of CBR slots released + * + * Date first: 12/03/2000 last: 12/03/2000 + */ +static int +idt_slots_rem(IDT * idt, TX_QUEUE * queue) +{ + int qptr, qmax; + int slots; + + qmax = idt->txslots_max; + slots = 0; + + for (qptr = 0; qptr < qmax; qptr++) { + if (idt->cbr_slot[qptr] != queue) + continue; + idt->cbr_slot[qptr] = NULL; + nicstar_sram_wr(idt, qptr + IDT_TST_START, 1, 0x40000000, 0, 0, 0); + slots++; + } + return (slots); +} + +/******************************************************************************* + * + * Initialize slots in CBR table + * + * in: IDT device + * out: zero = success + * + * Date first: 11/29/2000 last: 11/29/2000 + */ +static int +idt_slots_init(IDT * idt) +{ + int start; /* table start pointer */ + int qptr; + + start = IDT_TST_START; + + /* first, fill up the TX CBR table with 'VBR' entries */ + + for (qptr = 0; qptr < idt->txslots_max; qptr++) { + idt->cbr_slot[qptr] = NULL; + nicstar_sram_wr(idt, qptr + start, 1, 0x40000000, 0, 0, 0); + } + + /* now write the jump back to the table start */ + + nicstar_sram_wr(idt, qptr + start, 1, 0x60000000 | start, 0, 0, 0); + + return (0); +} + +/******************************************************************************* + * + * Open output queue for connection + * + * in: IDT device, connection (class, traf_pcr, & traf_scr fields valid) + * out: zero = success + * + * Date first: 11/29/2000 last: 06/13/2001 + */ + +int +idt_connect_txopen(IDT * idt, CONNECTION * connection) +{ + TX_QUEUE *txqueue; + int cellrate; + int cbr_slots; + int s; + + cellrate = connection->traf_scr; /* 06/13/2001 use SCR instead + * of PCR */ + + if (connection->class == T_ATM_UBR) { /* UBR takes whatever is left + * over */ + connection->queue = &idt->queue_ubr; + if (idt_sysctl_logvcs) + printf("idt_connect_txopen: UBR connection for %d/%d\n", + connection->vpi, connection->vci); + return (0); + } + if (connection->class == T_ATM_ABR) { /* ABR treated as UBR-plus */ + connection->queue = &idt->queue_abr; + if (idt_sysctl_logvcs) + printf("idt_connect_txopen: UBR+ connection for %d/%d\n", + connection->vpi, connection->vci); + return (0); + } + if (connection->class == T_ATM_CBR) { + cbr_slots = idt_slots_cbr(idt, cellrate); + s = splimp(); + if (cbr_slots > (idt->txslots_max - idt->txslots_cur) || + idt->cbr_freect < 1) { + splx(s); + return (1); /* requested rate not available */ + } + idt->txslots_cur += cbr_slots; + idt->cellrate_tcur += cellrate; + idt->cbr_freect--; + txqueue = idt->cbr_free[idt->cbr_freect]; + txqueue->rate = cellrate; /* was connection->traf_pcr */ + + if (idt_slots_add(idt, txqueue, cbr_slots)) { + idt->txslots_cur -= cbr_slots; /* cannot add CBR slots */ + idt->cellrate_tcur -= cellrate; + idt->cbr_free[idt->cbr_freect] = txqueue; + idt->cbr_freect++; + splx(s); + return (1); + } + splx(s); + if (idt_sysctl_logvcs) + printf("idt_connect_txopen: CBR connection for %d/%d\n", + connection->vpi, connection->vci); + connection->queue = txqueue; + } + if (connection->class == T_ATM_VBR) { + txqueue = &idt->queue_vbr; + connection->queue = txqueue; + txqueue->rate += connection->traf_scr; /* from traf_pcr + * 12/17/2000 */ + if (idt_sysctl_logvcs) + printf("idt_connect_txopen: VBR connection for %d/%d\n", + connection->vpi, connection->vci); + } + idt_connect_newvbr(idt);/* recalculate VBR divisor values */ + + if (connection->class == T_ATM_CBR || + connection->class == T_ATM_VBR) + return (0); + + return (1); /* unknown class */ +} + +/******************************************************************************* + * + * Close connection output queue + * + * in: IDT device, connection (class, traf_pcr, & traf_scr fields valid) + * out: zero = success + * + * Date first: 12/03/2000 last: 12/03/2000 + */ +int +idt_connect_txclose(IDT * idt, CONNECTION * connection) +{ + TX_QUEUE *txqueue; + int cellrate; + int slots; + int s; + + cellrate = connection->traf_pcr; + txqueue = connection->queue; + if (idt_sysctl_logvcs) + printf("idt_connect_txclose: closing connection for %d/%d\n", + connection->vpi, connection->vci); + + idt_queue_flush(connection); /* flush all connection mbufs */ + + if (connection->class == T_ATM_UBR || /* UBR takes whatever is left + * over */ + connection->class == T_ATM_ABR) { /* ABR not supported, use UBR */ + connection->queue = NULL; + return (0); + } + if (connection->class == T_ATM_CBR) { + slots = idt_slots_rem(idt, txqueue); /* remove this queue + * from CBR slots */ + s = splimp(); + idt->txslots_cur -= slots; + idt->cellrate_tcur -= cellrate; + if (txqueue != NULL) { /* 06/12/2001 check for failure on + * open */ + idt->cbr_free[idt->cbr_freect] = txqueue; + idt->cbr_freect++; + } + splx(s); + connection->queue = NULL; + } + if (connection->class == T_ATM_VBR) { + txqueue = &idt->queue_vbr; + connection->queue = NULL; + txqueue->rate -= connection->traf_scr; /* from traf_pcr + * 12/17/2000 */ + } + idt_connect_newvbr(idt);/* recalculate VBR divisor values */ + + if (connection->class == T_ATM_CBR || + connection->class == T_ATM_VBR) + return (0); + + return (1); /* unknown class */ +} + +/******************************************************************************* + * + * Calculate new VBR divisor values + * + * in: IDT device + * + * Date first: 12/03/2000 last: 12/03/2000 + */ +static void +idt_connect_newvbr(IDT * idt) +{ + TX_QUEUE *txqueue; + int rate_newvbr; + int rate_noncbr; + int divisor; + + txqueue = &idt->queue_vbr; + + rate_newvbr = txqueue->rate; + rate_noncbr = idt->cellrate_tmax - idt->cellrate_tcur; + + if (rate_newvbr < 1) /* keep sane and prevent divide by zero */ + rate_newvbr = 1; + + if (rate_newvbr >= rate_noncbr) { + txqueue->vbr_m = 1; + txqueue->vbr_n = 1; + return; + } + divisor = rate_newvbr * 1000; /* size of lookup table */ + divisor += rate_newvbr >> 1; /* apply rounding to divide */ + divisor /= rate_noncbr; /* always < 1000, since newvbr < noncbr */ + + if (idt_sysctl_logvcs) + printf("idt_connect_newvbr: divisor=%d\n", divisor); + txqueue->vbr_m = vbr_div_m[divisor]; + txqueue->vbr_n = vbr_div_n[divisor]; + if (idt_sysctl_logvcs) + printf("idt_connect_newvbr: m=%d, n=%d\n", txqueue->vbr_m, txqueue->vbr_n); +} + +/* For VBR, we track the sum of all the VBR peak cellrates, and divide + * that from the "remaining" bandwidth, which is total minus current CBR. + * + * We will need to adjust the VBR divisor whenever we add a CBR or VBR. + * + * Because of the integer scalign (1000) preload, the cellrate for the + * VBR channel should not exceed 2 million (aprox 5 OC3s). This is + * protected by the check for rate_newvbr >= rate_noncbr. + * + ******************************************************************************* + * + * Initialize large buffers, indexes, and reference counts + * + * in: IDT device + * out: zero = success + * + * Date first: 11/01/2000 last: 05/25/2001 + */ + +int +idt_buffer_init(IDT * idt) +{ + + idt->raw_headm = NULL; /* nicstar_add_buf() will initialize */ + idt->raw_headp = 0; + + return (0); +} + +/******************************************************************************* + * + * Get large buffer from kernel pool + * + * out: mbuf, NULL = error + * + * Date first: 05/25/2001 last: 05/25/2001 + */ + +struct mbuf * +idt_mbufcl_get(void) +{ + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + return (m); + + m_freem(m); + return (NULL); +} + +/******************************************************************************* + * + * Initialize connection table + * + * in: IDT, number of VPI bits (0, 1, or 2) + * out: zero = success + * + * Date first: 10/29/2000 last: 12/10/2000 + */ + +int +idt_connect_init(IDT * idt, int vpibits) +{ + CONNECTION *connection; + int pages; + int vpi; + int vci; + + switch (vpibits) { + case 1: + idt->conn_maxvpi = 2; + idt->conn_maxvci = 2048; + break; + case 2: + idt->conn_maxvpi = 4; + idt->conn_maxvci = 1024; + break; + default: + idt->conn_maxvpi = 1; + idt->conn_maxvci = 4096; + } + + pages = (sizeof(CONNECTION) * MAX_CONNECTION) + PAGE_SIZE - 1; + pages /= PAGE_SIZE; + idt->connection = (CONNECTION *) vm_page_alloc_contig(pages * PAGE_SIZE, + 0x100000, 0xffffffff, 0x2000); + if (idt->connection == NULL) + return (1); + + for (vpi = 0; vpi < idt->conn_maxvpi; vpi++) + for (vci = 0; vci < idt->conn_maxvci; vci++) { + connection = &idt->connection[vpi * idt->conn_maxvci + vci]; + connection->vccinf = NULL; /* may want to change to + * "unclaimed" */ + connection->status = 0; /* closed */ + connection->vpi = vpi; + connection->vci = vci; + connection->queue = NULL; /* no current TX queue */ + connection->recv = NULL; /* no current receive + * mbuf */ + connection->rlen = 0; + connection->maxpdu = 0; + connection->traf_pcr = 0; + connection->traf_scr = 0; + connection->aal = 0; + connection->class = 0; + connection->flg_mpeg2ts = 0; + connection->flg_clp = 0; + } + + return (0); +} + +/******************************************************************************* + * + * Look up a connection + * + * in: IDT, vpi, vci + * out: CONNECTION, NULL=invalid vpi/vci + * + * Date first: 10/29/2000 last: 10/29/2000 + */ + +CONNECTION * +idt_connect_find(IDT * idt, int vpi, int vci) +{ + if (vpi >= idt->conn_maxvpi) + return (NULL); + if (vci >= idt->conn_maxvci) + return (NULL); + + return (&idt->connection[vpi * idt->conn_maxvci + vci]); +} + +/****************************************************************************** + * + * MBUF SECTION + * + ****************************************************************************** + * + * Align data in mbuf (to 32-bit boundary) + * + * in: mbuf + * out: zero = success + * + * Date first: 11/08/2000 last: 11/15/2000 + */ + +int +idt_mbuf_align(struct mbuf * m, struct mbuf * prev) +{ + caddr_t buf_base; + int buf_size; + int offset; + int newlen; + int count; + + if (m == NULL) + return (1); + if (((int)m->m_data & 3) == 0) + return (0); + + if (m->m_flags & M_EXT) { /* external storage */ + buf_base = m->m_ext.ext_buf; + buf_size = m->m_ext.ext_size; + + /* + * we should really bail out at this point, since we cannot + * just shift the data in an external mbuf + */ + + } else { + if (m->m_flags & M_PKTHDR) { /* internal storage, packet + * header */ + buf_base = m->m_pktdat; + buf_size = MHLEN; + } else { + buf_base = m->m_dat; /* internal storage, no packet + * header */ + buf_size = MLEN; + } + } + offset = 4 - ((int)buf_base & 3); + offset &= 3; + buf_base += offset; /* new (aligned) buffer base */ + + if (m->m_len + offset > buf_size) /* not enough space to just + * move */ + if (prev != NULL) + if (idt_mbuf_append4(prev, m->m_data) == 0) { /* give word to prev + * mbuf */ + m->m_data += 4; + m->m_len -= 4; + } + if (m->m_len + offset > buf_size) /* still not enough space */ + if (m->m_next != NULL) { + newlen = buf_size - offset; /* maximum new length */ + newlen &= 0xfffffc; /* fix the length too... */ + count = buf_size - newlen; /* bytes we have to get + * rid of */ + if (idt_mbuf_prepend(m->m_next, m->m_data + newlen, count) == 0) + m->m_len = newlen; + } + if (m->m_len + offset > buf_size) /* we're stuck... */ + return (1); + + bcopy(m->m_data, buf_base, m->m_len); /* move data to aligned + * position */ + m->m_data = buf_base; + return (0); +} + +/******************************************************************************* + * + * Append 4 bytes to mbuf + * + * in: mbuf, data pointer + * out: zero = success + * + * Date first: 11/08/2000 last: 12/13/2000 + */ + +int +idt_mbuf_append4(struct mbuf * m, char *newdata) +{ + caddr_t buf_base; + int buf_size; + int align; + int space; + + if (m == NULL) + return (1); + + if (m->m_flags & M_EXT) /* external storage */ + return (1); /* 12/13/2000 we must not touch it */ + + if (m->m_flags & M_PKTHDR) { /* internal storage, packet header */ + buf_base = m->m_pktdat; + buf_size = MHLEN; + } else { + buf_base = m->m_dat; /* internal storage, no packet header */ + buf_size = MLEN; + } + + align = (4 - ((int)buf_base & 3)) & 3; + buf_base += align; + buf_size -= align; + buf_size &= 0xfffffc; + + space = buf_size - m->m_len; + if (space < 4) /* enough space to add 4 bytes? */ + return (1); + + space -= m->m_data - buf_base; /* get space at end */ + + if (space < 4) { + bcopy(m->m_data, buf_base, m->m_len); + m->m_data = buf_base; + } + bcopy(newdata, m->m_data + m->m_len, 4); + m->m_len += 4; + + return (0); +} + +/******************************************************************************* + * + * Get current base of data storage + * + * in: mbuf + * out: base + * + * Date first: 11/16/2000 last: 11/16/2000 + */ + +caddr_t +idt_mbuf_base(struct mbuf * m) +{ + if (m == NULL) + return (NULL); + + if (m->m_flags & M_EXT) /* external storage */ + return (m->m_ext.ext_buf); + + if (m->m_flags & M_PKTHDR) /* internal storage, packet header */ + return (m->m_pktdat); + + return (m->m_dat); /* internal storage, no packet header */ +} + +/******************************************************************************* + * + * Copy mbuf chain to new chain (aligned) + * + * in: mbuf + * out: new mbuf chain, NULL=error + * + * Date first: 11/19/2000 last: 05/25/2001 + */ + +struct mbuf * +idt_mbuf_copy(IDT * idt, struct mbuf * m) +{ + struct mbuf *nbuf, *dbuf, *sbuf; + u_char *sptr; + int slen; + int clen; + + nbuf = idt_mbufcl_get(); + if (nbuf == NULL) + return (NULL); + dbuf = nbuf; + dbuf->m_len = 0; + + for (sbuf = m; sbuf != NULL; sbuf = sbuf->m_next) { + sptr = sbuf->m_data; + slen = sbuf->m_len; + while (slen) { + clen = slen; + if (clen > NICSTAR_LRG_SIZE - dbuf->m_len) + clen = NICSTAR_LRG_SIZE - dbuf->m_len; + bcopy(sptr, dbuf->m_data + dbuf->m_len, clen); + sptr += clen; + slen -= clen; + dbuf->m_len += clen; + if (dbuf->m_len >= NICSTAR_LRG_SIZE) { + dbuf->m_next = idt_mbufcl_get(); + if (dbuf->m_next == NULL) { + m_freem(nbuf); + return (NULL); + } + dbuf = dbuf->m_next; + dbuf->m_len = 0; + } /* if need dest buf */ + } /* while(slen) */ + } /* for... source buf */ + m_freem(m); + return (nbuf); +} + +/******************************************************************************* + * + * Prepend data to mbuf (no alignment done) + * + * in: mbuf, data pointer, data length + * out: zero = success + * + * Date first: 11/15/2000 last: 12/13/2000 + */ + +int +idt_mbuf_prepend(struct mbuf * m, char *newdata, int newlen) +{ + caddr_t buf_base; + int buf_size; + int space; + + if (m == NULL) + return (1); + + if (m->m_flags & M_EXT) /* external storage */ + return (1); /* 12/13/2000 we must not touch it */ + + if (m->m_flags & M_PKTHDR) { /* internal storage, packet header */ + buf_base = m->m_pktdat; + buf_size = MHLEN; + } else { + buf_base = m->m_dat; /* internal storage, no packet header */ + buf_size = MLEN; + } + + space = m->m_data - buf_base; + + if (space >= newlen) { /* already space at head of mbuf */ + m->m_data -= newlen; + m->m_len += newlen; + bcopy(newdata, m->m_data, newlen); + return (0); + } + space = buf_size - m->m_len; /* can we get the space by shifting? */ + if (space < newlen) + return (1); + + bcopy(m->m_data, m->m_data + newlen, m->m_len); + bcopy(newdata, m->m_data, newlen); + m->m_len += newlen; + + return (0); +} + +/******************************************************************************* + * + * Get amount of data used in mbuf chain + * + * in: mbuf chain + * out: used space + * + * Date first: 11/10/2000 last: 11/10/2000 + */ + +int +idt_mbuf_used(struct mbuf * mfirst) +{ + struct mbuf *m1; + int mbuf_used; + + mbuf_used = 0; /* used mbuf space */ + + for (m1 = mfirst; m1 != NULL; m1 = m1->m_next) + mbuf_used += m1->m_len; + + return (mbuf_used); +} + +/******************************************************************************* + * + * Notes on transmit buffers: + * + * According to the IDT Nicstar User Manual (version 1.0 2/26/1997), we must + * follow these rules for the transmit buffers (page 66): + * + * 1. The buffer length must not be zero. + * 2. The buffer length must be a multiple of four bytes. + * 3. The sum of the buffer lengths must be a multiple of 48 bytes if + * it is a CS-PDU (eg AAL5). + * 4. All buffers for a CS-PDU must be contiguous and grouped (no other + * PDU buffers or even TSRs). + * 5. For AAL5 PDUs, the buffer lengths must include 8 bytes for the + * AAL5 length/control and CRC fields. + * 6. For AAL5 PDUs, the buffer length of the last buffer must be > 8 bytes. + * 7. For AAL5 PDUs, all buffers containing bytes for the last cell must + * have the END_CS_PDU bit set to 1. + * + * Also, from the IDT applications note ("FAQ") 77211_AN_97088.pdf file: + * Page 5, under "General Technical Questions" (copied EXACTLY): + * + * 5). Can the NicStar begin segmentation from a non-word aligned buffer? + * No, the transmit buffer must point to a word aligned buffer. + * + * Since the buffers MUST be word aligned and MUST be word lengths, we have + * two potential problems with M_EXT mbufs: + * + * 1. If the M_EXT mbuf has a non word aligned address, we have to copy + * the whole thing to a fresh buffer. Unless - the previous mbuf is + * not M_EXT, and it is short by exactly the same amount. Unlikely. + * + * 2. If the M_EXT mbuf has a non word length, we have to push those bytes + * to the next mbuf. If the next mbuf is also M_EXT, we are stuck. + * Unless - the extra bytes from both mbufs are exactly 4 bytes. Then + * we can MGET an empty buf to splice in between. + * + * Also, these rules mean that if any buffer is not word-length, all of the + * following buffers will need to be copied/shifted, unless one or more have + * lengths off by the right amount to fix the earlier buffer. + * + ******************************************************************************* + * + * Put mbuf chain on transmit queue + * + * in: IDT device, mbuf chain, vpi, vci, flags (2 MPEG2 TS == 8 AAL5 cells) + * out: (nothing) + * + * Date first: 11/08/2000 last: 05/30/2000 + */ + +void +idt_transmit(IDT * idt, struct mbuf * mfirst, int vpi, int vci, int flags) +{ + CONNECTION *connection; + struct mbuf *m1, *m0, *malign, *msend; + int tot_size, tot_scq, x; + int this_len; + int padding; + + connection = idt_connect_find(idt, vpi, vci); + if (connection == NULL) { /* this VPI/VCI not open */ + idt_transmit_drop(idt, mfirst); + return; + } + if (connection->queue == NULL) { + idt_transmit_drop(idt, mfirst); + connection->vccinf->vc_oerrors++; + return; + } + if (flags) + connection->flg_mpeg2ts = 1; + else + connection->flg_mpeg2ts = 0; + + /* + * New strategy: assume that all the buffers are aligned and word + * length. Drop out and handle exceptions below. + */ + + tot_size = 0; + tot_scq = 1; + malign = NULL; + + for (m1 = mfirst; m1 != NULL; m1 = m1->m_next) { + this_len = m1->m_len; + tot_size += this_len; + tot_scq++; + if (malign != NULL) + continue; + if ((int)(m1->m_data) & 3) { /* bad alignment */ + malign = m1; + continue; + } + if ((this_len & 3) == 0) /* mbuf length is ok */ + continue; + if (m1->m_next != NULL) { /* bad length (in middle) */ + malign = m1; + continue; + } + padding = 4 - (this_len & 3); + tot_size += padding; + m1->m_len += padding; + break; /* last mbuf, so avoid the loop test */ + } + if (malign == NULL) { /* perfect packet, no copy needed */ + mfirst->m_pkthdr.len = tot_size; + if (connection->flg_mpeg2ts) + tot_scq += tot_size / 376; /* more entries needed + * for split */ + mfirst->m_pkthdr.csum_data = tot_scq; + + if (idt_queue_put(connection, mfirst)) /* put packet on TX + * queue */ + device_printf(idt->dev, "Cannot queue packet for %d/%d.\n", vpi, vci); + if (connection->queue->mget == mfirst) /* was the queue empty? */ + idt_transmit_top(idt, connection->queue); /* IFF empty, prime it + * now */ + return; + } + /* + * Bad alignment or length, so fall through to old code... The first + * alignment problem is at 'malign' + */ + if (idt_sysctl_logvcs) + device_printf(idt->dev, "Bad TX buf alignment, len=%d.\n", tot_size); + + if (idt_mbuf_align(mfirst, NULL)) { + printf("idt_transmit: cannot align first mbuf.\n"); + idt_transmit_drop(idt, mfirst); + connection->vccinf->vc_oerrors++; + return; + } + /* find first mbuf with bad alignment (if any) */ + + m0 = mfirst; + for (m1 = mfirst->m_next; m1 != NULL; m0 = m1, m1 = m1->m_next) { + if (m1->m_len & 3) + break; + if ((int)(m1->m_data) & 3) + break; + } + if (m1 != NULL) { + m1 = idt_mbuf_copy(idt, m1); /* copy the rest into new + * mbufs */ + m0->m_next = m1; + if (m1 == NULL) { + printf("idt_transmit: could not copy buffers.\n"); + idt_transmit_drop(idt, mfirst); + connection->vccinf->vc_oerrors++; + return; /* FIX THIS - this path has been taken */ + } + } + msend = mfirst; + + /* The mbuf chain is aligned, now we need to pad to word length */ + + tot_size = idt_mbuf_used(msend); /* forget the pkthdr length... */ + msend->m_pkthdr.len = tot_size; + + padding = (4 - (tot_size & 3)) & 3; + if (padding) { + for (m1 = msend; m1->m_next != NULL; m1 = m1->m_next); + m1->m_len += padding; + } + x = 1; /* now calculate the SCQ entries needed */ + for (m1 = msend; m1 != NULL; m1 = m1->m_next) + x++; + if (connection->flg_mpeg2ts) + x += tot_size / 376; /* more entries needed for split */ + msend->m_pkthdr.csum_data = x; + + /* now we have an mbuf chain, from *msend to *m1 ready to go */ + + if (idt_queue_put(connection, msend)) /* put packet on TX queue */ + device_printf(idt->dev, "Cannot queue packet for %d/%d.\n", vpi, vci); + + if (connection->queue->mget == msend) /* was the queue empty? */ + idt_transmit_top(idt, connection->queue); /* IFF empty, prime it + * now */ +} + +/* Notes on mbuf usage in the transmit queue: + * + * m_pkthdr.rcvif Connection pointer (set by idt_queue_put) + * m_pkthdr.len Length of PDU + * m_pkthdr.header TX queue pointer (06/01/2001) + * m_pkthdr.csum_flags Unused, keep zero + * m_pkthdr.csum_data Number of SCQ entries needed or used + * m_pkthdr.aux Unused, keep NULL + * + ******************************************************************************* + * + * Drop transmit mbuf chain and update counters + * + * in: IDT device, mbuf chain + * out: (nothing) + * + * Date first: 11/08/2000 last: 11/08/2000 + */ + +void +idt_transmit_drop(IDT * idt, struct mbuf * mfirst) +{ + struct mbuf *next; + int mesglen; + + mesglen = 0; + while (mfirst != NULL) { + mesglen += mfirst->m_len; + next = m_free(mfirst); + mfirst = next; + } + device_printf(idt->dev, "dropping transmit packet, size=%d\n", mesglen); + idt->stats_oerrors++; /* 12/15/2000 */ +} + +/******************************************************************************* + * + * Put mbuf chain on transmit queue + * + * in: IDT device, TX_QUEUE + * out: (nothing) + * + * Date first: 12/03/2000 last: 06/01/2001 + */ + +void +idt_transmit_top(IDT * idt, TX_QUEUE * txqueue) +{ + CONNECTION *connection; + struct mbuf *top, *m; + static int padding[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int scq_space; + int val, val1, val3, val4; + int count, mlen, tlen, pad; + char *mptr; + int pdulen; + int vci, vpi; + int s; + + if (txqueue == NULL) /* 12/12/2000 */ + return; + if (txqueue->mget == NULL) /* check for empty queue */ + return; + + s = splimp(); + + scq_space = txqueue->scq_len - txqueue->scq_cur; + + /* Now we can add the queue entries for the PDUs */ + + count = 0; + + for (;;) { + top = txqueue->mget; /* next available mbuf */ + if (top == NULL) + break; + + if (top->m_pkthdr.csum_data + 4 > scq_space) + break; /* not enough space for this PDU */ + + top = idt_queue_get(txqueue); + if (top == NULL) + break; + connection = (CONNECTION *) top->m_pkthdr.rcvif; + vpi = connection->vpi; + vci = connection->vci; + top->m_pkthdr.header = (void *)connection->queue; + + top->m_pkthdr.csum_data = 0; /* track actual number of SCQ + * entries used */ + + tlen = top->m_pkthdr.len; + switch (connection->aal) { + case ATM_AAL0: + val = 0; + break; + case ATM_AAL3_4: + val = 0x04000000; + break; + case ATM_AAL5: + val = 0x08000000; + break; + default: + device_printf(idt->dev, "bad AAL for %d/%d\n", vpi, vci); + m_freem(top); + connection->vccinf->vc_oerrors++; + continue; + } + val |= txqueue->vbr_m << 23; + val |= txqueue->vbr_n << 16; + val4 = (vpi << 20) | (vci << 4); + if (connection->flg_clp) + val4 |= 1; /* set CLP flag */ + + /* + * Now we are ready to start mapping the mbuf(s) to transmit + * buffer descriptors. If the MPEG2TS flag is set, we want + * to create AAL5 PDUs of exactly 384 data bytes each. + */ + + pdulen = top->m_pkthdr.len; /* default case: don't split + * PDU */ + pad = 0; + if (connection->flg_mpeg2ts) { + if ((pdulen % 376) == 0) { /* correct multiple */ + pdulen = 376; /* cut off every pdu at 374 + * data bytes */ + pad = 8; + } else + device_printf(idt->dev, "Bad MPEG2 PDU buffer (%d bytes).\n", pdulen); + } + val3 = pdulen; /* actual (unpadded) PDU length */ + + pdulen += (4 - (pdulen & 3)) & 3; + + if (pad == 0) { /* normal padding (PDU not split) */ + pad = pdulen; + if (connection->aal == ATM_AAL5) + pad += 8; + pad = 48 - (pad % 48); + if (pad == 48) + pad = 0; + if (connection->aal == ATM_AAL5) + pad += 8; /* pad of up to 52 is + * possible/neccessary */ + } + tlen = 0; + for (m = top; m != NULL; m = m->m_next) { + while ((mlen = m->m_len)) { + if (mlen + tlen > pdulen) + mlen = pdulen - tlen; /* how much of this + * buffer can we use? */ + mptr = m->m_data; + tlen += mlen; /* length of this PDU */ + m->m_len -= mlen; /* bytes remaining in + * mbuf */ + m->m_data += mlen; /* new data pointer */ + + val1 = val; + if (tlen > pdulen + pad - 48) /* is this buffer in the + * last cell? */ + val1 |= 0x40000000; /* last buffer in PDU */ + + if (tlen == pdulen) { /* end of PDU, so figure + * padding needed */ + idt->stats_opdus++; /* 12/15/2000 */ + idt->stats_obytes += pdulen; /* 12/15/2000 */ + connection->vccinf->vc_opdus++; + connection->vccinf->vc_obytes += pdulen; + tlen = 0; + if (pad <= 8) + mlen += pad; /* just "add" padding to + * this buffer */ + } + *txqueue->scq_next++ = val1 | mlen; + *txqueue->scq_next++ = vtophys(mptr); + *txqueue->scq_next++ = val3; + *txqueue->scq_next++ = val4; + if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4) + txqueue->scq_next = txqueue->scq_base; + scq_space--; + top->m_pkthdr.csum_data++; /* 12/22/2000 */ + + /* + * if we need more than 8 bytes of padding, + * use the zero-filled buffer defined above. + */ + if (tlen == 0 && pad > 8) { /* end of PDU, do we + * need padding? */ + val1 |= 0x40000000; /* last buffer in PDU */ + *txqueue->scq_next++ = val1 | pad; + *txqueue->scq_next++ = vtophys(padding); + *txqueue->scq_next++ = val3; + *txqueue->scq_next++ = val4; + if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4) + txqueue->scq_next = txqueue->scq_base; + scq_space--; + top->m_pkthdr.csum_data++; /* 12/22/2000 */ + } + } + } + + /* + * Now that we have set up the descriptors, add the entry + * for Transmit Status Request so we know when the PDU(s) + * are done. + */ + + *txqueue->scq_next++ = 0xa0000000; /* TSR with interrupt */ + *txqueue->scq_next++ = (u_long)top; + *txqueue->scq_next++ = 0; + *txqueue->scq_next++ = 0; + + if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4) + txqueue->scq_next = txqueue->scq_base; + scq_space--; + top->m_pkthdr.csum_data++; /* 12/22/2000 */ + count++; + + txqueue->scq_cur += top->m_pkthdr.csum_data; + } + + /* + * 05/31/2001: Optimization: Since writing to SRAM is very + * expensive, we will only do this when the pointer is stale (half + * of the queue). If the queue is less than 1/4 full, then write the + * pointer anyway. + */ + + if (idt_sysctl_qptrhold) { + scq_space = txqueue->scq_next - txqueue->scq_last; /* number pending */ + scq_space /= 4; + if (scq_space < 0) + scq_space += txqueue->scq_len; + if (scq_space * 2 < txqueue->scq_len && /* less than half + * pending */ + txqueue->scq_cur > txqueue->scq_len / 4) /* and queue is active */ + count = 0; + } + if (count) { /* we need to update the queue pointer */ + nicstar_sram_wr(idt, txqueue->scd, 1, vtophys(txqueue->scq_next), 0, 0, 0); + txqueue->scq_last = txqueue->scq_next; + } + splx(s); + + return; +} + +/* Once a packet has been put in the Segmentation Channel Queue, it will + * be sent, and then the mbuf will harvested by idt_intr_tsq(). While it + * is in the SCQ, m_pkthdr.header is the pointer to the TX queue. This is + * important because if the connection is closed while there are still + * mbufs in the SCQ, idt_intr_tsq() still needs to update the TX queue. + * + ****************************************************************************** + * + * Handle entries in Transmit Status Queue (end of PDU interrupt or TSQ full) + * + * in: IDT device + * + * Date first: 12/04/2000 last: 06/10/2001 + */ +static void +idt_intr_tsq(IDT * idt) +{ + CONNECTION *connection; + TX_QUEUE *txqueue; + u_long *tsq_ptr; + u_long val; + struct mbuf *m; + int count, s; + + s = splimp(); + + tsq_ptr = idt->tsq_head; + + count = 0; + while ((tsq_ptr[1] & 0x80000000) == 0) { + m = (struct mbuf *) tsq_ptr[0]; + if (m != NULL) {/* first test for timer rollover entry */ + if (((int)m & 0x000000ff)) /* now do sanity check + * on the mbuf ptr */ + device_printf(idt->dev, + "DANGER! bad mbuf (%x), stamp=%x\n", + (int)m, (int)tsq_ptr[1]); + else { + connection = (CONNECTION *) m->m_pkthdr.rcvif; + txqueue = (TX_QUEUE *) m->m_pkthdr.header; + txqueue->scq_cur -= m->m_pkthdr.csum_data; + if (txqueue->scq_cur < 0 || txqueue->scq_cur > txqueue->scq_len) + device_printf(idt->dev, "DANGER! scq_cur is %d\n", txqueue->scq_len); + m->m_pkthdr.header = NULL; + m_freem(m); + idt_transmit_top(idt, txqueue); /* move more into queue */ + } + } + tsq_ptr[0] = 0; + tsq_ptr[1] = 0x80000000; /* reset TSQ entry */ + tsq_ptr += 2; + if (tsq_ptr >= idt->tsq_base + idt->tsq_size * 2) + tsq_ptr = idt->tsq_base; + count++; + } + idt->tsq_head = tsq_ptr; + + if (count) { + val = (int)tsq_ptr - (int)idt->tsq_base; + val -= 8; /* always stay one behind */ + val &= 0x001ff8; + *idt->reg_tsqh = val; + } + splx(s); +} + +/* There is a problem with the pointer rollover where the SAR will think the + * TSQ buffer is full (forever?) unless we hold the head pointer back. + * This is not mentioned in the 77211 docs, but is a resolved issue in + * revision D of the 77252 chips (see 77252 errata). + * + * If a connection is closed while there are still mbufs in the TX queue, + * the connection TX queue pointer will be NULL. That is why we have a + * special copy of the pointer in m_pkthdr.header. Also, idt_transmit_top() + * will allow the TX queue for that connection to empty properly. + * + * It is possible for a TSQ entry to be 0x00ffffff/0x00ffffff, which is + * obviously not an mbuf and not a timer rollover entry. We now have an + * mbuf sanity check for this. + * + ****************************************************************************** + * + * nicstar_itrx ( card ) + * + * service error in transmitting PDU interrupt. + * +*/ +static void +nicstar_itrx(nicstar_reg_t * idt) +{ + /* trace mbuf and release */ +} + +/****************************************************************************** + * + * Raw cell receive interrupt + * + * service raw cell reception interrupt. + * + */ + +static void +nicstar_rawc(nicstar_reg_t * idt) +{ + u_long ptr_tail; + struct mbuf *qmbuf; + u_long *qptr; + u_long next_mbuf; + u_long next_phys; + + if (idt->raw_headm == NULL || + idt->raw_headp == 0) { + device_printf(idt->dev, + "RAW cell received, buffers not ready (%x/%x).\n", + (int)idt->raw_headm, (int)idt->raw_headp); + return; + } + ptr_tail = *(volatile u_long *)(idt->virt_baseaddr + REGRAWT); + if ((ptr_tail & 0xfffff800) == idt->raw_headp) + return; /* still in the same large buffer */ + + if ((ptr_tail & 0x7ff) < 64) /* wait until something in new buffer */ + return; + + qmbuf = idt->raw_headm; + qptr = (u_long *)qmbuf->m_data; + + next_mbuf = qptr[31 * 16 + 1]; /* next handle (virtual) */ + next_phys = qptr[31 * 16 + 0]; /* next physical address */ + + /* if we want to do anything with the raw data, this is the place */ + + idt_mcheck_rem(idt, qmbuf); + m_free(qmbuf); + + idt->raw_headm = (struct mbuf *) next_mbuf; + idt->raw_headp = next_phys; +} + +/***************************************************************************** + * + * Handle AAL5 PDU length + * + * in: IDT device, first mbuf in chain, last mbuf + * out: zero = success, nz = failure (mbuf chain freed) + * + * Date first: 11/18/2000 last: 12/14/2000 + */ + +int +idt_receive_aal5(IDT * idt, struct mbuf * mfirst, struct mbuf * mdata) +{ + struct mbuf *m2; + unsigned char *aal5len; + int plen; + int diff; + + aal5len = mdata->m_data + mdata->m_len - 6; /* aal5 length = 16 bits */ + plen = aal5len[0] * 256 + aal5len[1]; + diff = mfirst->m_pkthdr.len - plen; /* number of bytes to trim */ + + if (diff == 0) + return (0); + + if (diff < 0) { + device_printf(idt->dev, + "AAL5 PDU length (%d) greater than cells (%d), discarding\n", + plen, mfirst->m_pkthdr.len); + m_freem(mfirst); + return (1); + } + while (mdata->m_len < diff) { /* last mbuf not big enough */ + diff -= mdata->m_len; + m2 = mdata; + m_free(mdata); + if (mdata == mfirst) { /* we just tossed the whole PDU */ + device_printf(idt->dev, "AAL5 PDU length failed, discarding.\n"); + return (1); /* the packetheadr length was bad! */ + } + for (mdata = mfirst; mdata->m_next != m2; mdata = mdata->m_next); + mdata->m_next = NULL; /* remove old link to free'd mbuf */ + } + mdata->m_len -= diff; /* trim last mbuf */ + mfirst->m_pkthdr.len = plen; + + return (0); +} + +/* 12/14/2000: Removed "pruning" log message. + * + ***************************************************************************** + * + * nicstar_recv ( card ) + * + * rebuilds PDUs from entries in the Recieve Status Queue. + * + */ +struct rsq_entry { + u_long vpivci; + struct mbuf *mdata; + u_long crc; + u_long flags; +}; + +static void +nicstar_recv(nicstar_reg_t * idt) +{ + CONNECTION *connection; + volatile u_long *regh = (volatile u_long *)(idt->virt_baseaddr + REGRSQH); + struct rsq_entry *rsq; + struct mbuf *mdata, *mptr; + u_long flags; + u_long crc; + int vpi; + int vci; + int clen; + int x, s; + + s = splimp(); + + rsq = (struct rsq_entry *) (idt->fixbuf + 0x2000 + (idt->rsqh & 0x1ffc)); + + if ((rsq->flags & 0x80000000) == 0) { + splx(s); + return; + } + while (rsq->flags & 0x80000000) { + vpi = rsq->vpivci >> 16; /* first, grab the RSQ data */ + vci = rsq->vpivci & 0xffff; + mdata = rsq->mdata; + crc = rsq->crc; + flags = rsq->flags; + clen = (flags & 0x1ff) * 48; + + rsq->vpivci = 0;/* now recycle the RSQ entry */ + rsq->mdata = NULL; + rsq->crc = 0; + rsq->flags = 0; /* turn off valid bit */ + rsq++; + if (rsq == (struct rsq_entry *) (idt->fixbuf + 0x4000)) + rsq = (struct rsq_entry *) (idt->fixbuf + 0x2000); + + idt_mcheck_rem(idt, mdata); + + connection = idt_connect_find(idt, vpi, vci); + if (connection == NULL) { /* we don't want this PDU */ + printf("nicstar_recv: No connection %d/%d - discarding packet.\n", + vpi, vci); + m_free(mdata); /* throw mbuf away */ + continue; + } + mdata->m_len = clen; + + mptr = connection->recv; + if (mptr == NULL) { + if (mdata->m_flags & M_PKTHDR) + connection->recv = mdata; + else { + idt->stats_ierrors++; /* 12/15/2000 */ + connection->vccinf->vc_ierrors++; + m_free(mdata); + continue; + } + } else { + x = 0; + while (mptr->m_next != NULL) { /* find last mbuf in + * chain */ + mptr = mptr->m_next; + x++; + if (x > 25) + break; + } + if (x > 25) { + mptr = connection->recv; + printf("nicstar_recv: invalid mbuf chain - probable corruption!\n"); + m_free(mdata); + idt->stats_ierrors++; /* 12/15/2000 */ + connection->vccinf->vc_ierrors++; + connection->recv = NULL; + connection->rlen = 0; + continue; + } + mptr->m_next = mdata; + } + connection->rlen += clen; + + if (flags & 0x2000) { /* end of PDU */ + mptr = connection->recv; /* one or more mbufs + * will be here */ + clen = connection->rlen; /* length based on cell + * count */ + connection->recv = NULL; + connection->rlen = 0; + + mptr->m_pkthdr.len = clen; + mptr->m_pkthdr.rcvif = NULL; + mptr->m_nextpkt = NULL; + + if (mptr->m_pkthdr.aux != NULL) { + device_printf(idt->dev, + "received pkthdr.aux=%x\n", + (int)mptr->m_pkthdr.aux); + mptr->m_pkthdr.aux = NULL; + } + if (mptr->m_pkthdr.csum_flags) { + device_printf(idt->dev, + "received pkthdr.csum_flags=%x\n", + mptr->m_pkthdr.csum_flags); + mptr->m_pkthdr.csum_flags = 0; + } + if (flags & 0x200 && /* bad CRC */ + idt->flg_igcrc == 0) { + printf("nicstar_recv: Bad CRC - discarding PDU: %d/%d\n", vpi, vci); + idt->stats_ierrors++; /* 12/15/2000 */ + connection->vccinf->vc_ierrors++; + m_freem(mptr); + continue; + } + if (connection->aal == ATM_AAL5) { + if (idt_receive_aal5(idt, mptr, mdata)) /* adjust for AAL5 + * length */ + continue; + } + idt->stats_ipdus++; /* 12/15/2000 */ + idt->stats_ibytes += mptr->m_pkthdr.len; /* 12/15/2000 */ + connection->vccinf->vc_ipdus++; + connection->vccinf->vc_ibytes += mptr->m_pkthdr.len; + idt_receive(idt, mptr, vpi, vci); + } else if (connection->rlen > connection->maxpdu) { /* this packet is insane */ + printf("nicstar_recv: Bad packet, len=%d - discarding.\n", + connection->rlen); + connection->recv = NULL; + connection->rlen = 0; + idt->stats_ierrors++; /* 12/15/2000 */ + connection->vccinf->vc_ierrors++; + m_freem(mptr); + } /* end of PDU */ + } + + idt->rsqh = vtophys((u_long)rsq) & 0x1ffc; + *regh = (idt->rsqh - sizeof(struct rsq_entry)) & 0x1ff0; + + splx(s); +} + +/****************************************************************************** + * + * Physical Interrupt handler + * + * service phyical interrupt. + * + */ + +static void +nicstar_phys(nicstar_reg_t * idt) +{ + u_long t; + + if (idt->flg_le25) { + nicstar_util_rd(idt, 0x01, &t); /* get interrupt cause */ + if (t & 0x01) { + nicstar_util_wr(idt, 1, 0x02, 0x10); /* reset rx fifo */ + device_printf(idt->dev, "PHY cleared.\n"); + } + } else + device_printf(idt->dev, "Physical interrupt.\n"); +} + +/****************************************************************************** + * + * Status register values + */ + +#define STAT_REG_RSQAF 0x0002 /* receive status queue almost full */ +#define STAT_REG_LBMT 0x0004 /* large buffer queue empty */ +#define STAT_REG_SBMT 0x0008 /* small buffer queue empty */ +#define STAT_REG_RAWC 0x0010 /* raw cell interrupt */ +#define STAT_REG_EPDU 0x0020 /* end of PDU interrupt */ +#define STAT_REG_PHY 0x0400 /* physical interrupt */ +#define STAT_REG_TIME 0x0800 /* timer overflow interrupt */ +#define STAT_REG_TSQAF 0x1000 /* transmit status queue almost full */ +#define STAT_REG_TXIN 0x4000 /* TX PDU incomplete */ +#define STAT_REG_TXOK 0x8000 /* TX status indicator */ + +/****************************************************************************** + * + * Interrupt handler + * + * service card interrupt. + * + * nicstar_intr ( card ) + */ + +void +nicstar_intr(void *arg) +{ + IDT *idt; + volatile u_long stat_val, config_val; + int int_flags; + volatile int i; + + idt = (IDT *) arg; + + i = 0; + + config_val = *idt->reg_cfg; + stat_val = *idt->reg_stat; + + int_flags = + STAT_REG_TSQAF | /* transmit status queue almost full */ + STAT_REG_RSQAF | /* receive status queue almost full */ + STAT_REG_RAWC | /* raw cell interrupt */ + STAT_REG_EPDU | /* end of PDU interrupt */ + STAT_REG_TIME | /* timer overflow interrupt */ + STAT_REG_TXIN | /* TX PDU incomplete */ + STAT_REG_TXOK; /* TX status indicator */ + + if (idt->flg_le25) + int_flags |= STAT_REG_PHY; /* include flag for physical + * interrupt */ + + if (stat_val & (STAT_REG_LBMT | STAT_REG_SBMT)) { /* buffer queue(s) empty */ + if (stat_val & STAT_REG_SBMT) + device_printf(idt->dev, "small free buffer queue empty.\n"); + if (stat_val & STAT_REG_LBMT) + device_printf(idt->dev, "large free buffer queue empty.\n"); + nicstar_ld_rcv_buf(idt); + + if (*idt->reg_stat & STAT_REG_LBMT) { /* still empty, so + * disable IRQ */ + config_val &= ~0x01000000; + *idt->reg_cfg = config_val; + } + } + /* loop until no more interrupts to service */ + + while (stat_val & int_flags) { + i++; + if (i < 0 || i > 100) + break; + + *idt->reg_stat = stat_val & int_flags; /* clear status bits */ + + if (stat_val & STAT_REG_EPDU) { /* receive PDU */ + nicstar_recv(idt); + nicstar_ld_rcv_buf(idt); /* replace buffers, + * moved here 11/14/2000 */ + } + if (stat_val & STAT_REG_RAWC) { /* raw cell */ + nicstar_rawc(idt); + } + if (stat_val & STAT_REG_TXOK) { /* transmit complete */ + idt_intr_tsq(idt); + } + if (stat_val & STAT_REG_TXIN) { /* bad transmit */ + nicstar_itrx(idt); + device_printf(idt->dev, "Bad transmit.\n"); + } + if (stat_val & STAT_REG_TIME) { /* timer wrap */ + idt->timer_wrap++; + idt_intr_tsq(idt); /* check the TSQ */ + nicstar_recv(idt); /* check the receive queue */ + if (idt_sysctl_logbufs) + idt_status_bufs(idt); /* show the buffer + * status */ + } + if (stat_val & STAT_REG_PHY) { /* physical interrupt */ + nicstar_phys(idt); + *idt->reg_stat = STAT_REG_PHY; /* clear the int flag */ + } + if (stat_val & STAT_REG_RSQAF) { /* RSQ almost full */ + nicstar_recv(idt); + device_printf(idt->dev, "warning, RSQ almost full.\n"); + if (*idt->reg_stat & STAT_REG_RSQAF) { /* RSQ full */ + printf("RSQ is full, disabling interrupt.\n"); + config_val &= 0x00000800; + *idt->reg_cfg = config_val; + } + } + if (stat_val & STAT_REG_TSQAF) { /* TSQ almost full */ + idt_intr_tsq(idt); + device_printf(idt->dev, "warning, TSQ almost full.\n"); + if (*idt->reg_stat & STAT_REG_TSQAF) { + printf("TSQ is full, disabling interrupt.\n"); + config_val &= ~0x00000002; + *idt->reg_cfg = config_val; + } + } + stat_val = *idt->reg_stat; + } + if (i < 1 || i > 50) + device_printf(idt->dev, "i=%3d, status=%08x\n", i, (int)stat_val); +} diff --git a/sys/dev/idt/idt_harp.c b/sys/dev/idt/idt_harp.c new file mode 100644 index 0000000..ace90ef --- /dev/null +++ b/sys/dev/idt/idt_harp.c @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Matriplex, inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * This driver is derived from the Nicstar driver by Mark Tinguely, and + * some of the original driver still exists here. Those portions are... + * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely + * All rights reserved. + * + ****************************************************************************** + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/syslog.h> + +#include <sys/bus.h> +#include <sys/conf.h> + +#include <sys/module.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/netisr.h> +#include <net/if_var.h> + +#include <netatm/port.h> +#include <netatm/queue.h> +#include <netatm/atm.h> +#include <netatm/atm_sys.h> +#include <netatm/atm_sap.h> +#include <netatm/atm_cm.h> +#include <netatm/atm_if.h> +#include <netatm/atm_stack.h> +#include <netatm/atm_pcb.h> +#include <netatm/atm_var.h> +#include <netatm/atm_vc.h> + +#include <dev/idt/idtreg.h> +#include <dev/idt/idtvar.h> + +/****************************************************************************** + * + * HARP-specific definitions + * + */ + +#define IDT_DEV_NAME "idt" + +#define IDT_IFF_MTU 9188 +#define IDT_MAX_VCI 1023 /* 0 - 1023 */ +#define IDT_MAX_VPI 0 + +#define iv_next iv_cmn.cv_next +#define iv_toku iv_cmn.cv_toku +#define iv_upper iv_cmn.cv_upper +#define iv_vccb iv_cmn.cv_connvc /* HARP 3.0 */ +#define iv_state iv_cmn.cv_state +#define iu_pif iu_cmn.cu_pif +#define iu_unit iu_cmn.cu_unit +#define iu_flags iu_cmn.cu_flags +#define iu_mtu iu_cmn.cu_mtu +#define iu_open_vcc iu_cmn.cu_open_vcc +#define iu_instvcc iu_cmn.cu_instvcc /* HARP 3.0 */ +#define iu_vcc iu_cmn.cu_vcc +#define iu_vcc_zone iu_cmn.cu_vcc_zone +#define iu_nif_zone iu_cmn.cu_nif_zone +#define iu_ioctl iu_cmn.cu_ioctl +#define iu_openvcc iu_cmn.cu_openvcc +#define iu_closevcc iu_cmn.cu_closevcc +#define iu_output iu_cmn.cu_output +#define iu_config iu_cmn.cu_config + +/* + * ATM Interface services + */ +static struct stack_defn idt_svaal5 = { + NULL, + SAP_CPCS_AAL5, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +static struct stack_defn idt_svaal4 = { + &idt_svaal5, + SAP_CPCS_AAL3_4, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +static struct stack_defn idt_svaal0 = { + &idt_svaal4, + SAP_ATM, + SDF_TERM, + atm_dev_inst, + atm_dev_lower, + NULL, + 0, +}; +struct stack_defn *idt_services = &idt_svaal0; + +extern uma_zone_t idt_nif_zone; +extern uma_zone_t idt_vcc_zone; + +static int idt_atm_bearerclass(struct attr_bearer *); +static CONNECTION *idt_atm_harpconn(Cmn_unit *, Cmn_vcc *); +static int idt_atm_ioctl(int, caddr_t, caddr_t); + +static void idt_output(Cmn_unit *, Cmn_vcc *, KBuffer *); +static int idt_openvcc(Cmn_unit *, Cmn_vcc *); +static int idt_closevcc(Cmn_unit *, Cmn_vcc *); +static int idt_instvcc(Cmn_unit *, Cmn_vcc *); + +static void idt_recv_stack(void *, KBuffer *); + +/****************************************************************************** + * + * HARP GLUE SECTION + * + ****************************************************************************** + * + * Handle netatm core service interface ioctl requests + * + * Called at splnet. + * + * Arguments: + * code ioctl function (sub)code + * data data to/from ioctl + * arg optional code-specific argument + * + * Returns: + * 0 request processed successfully + * error request failed - reason code + * + */ +static int +idt_atm_ioctl(int code, caddr_t addr, caddr_t arg) +{ +#ifdef T_ATM_BUFQUEUE + CONNECTION *connection; + TX_QUEUE *txq; + struct mbuf *m; + Cmn_unit *cup; + Cmn_vcc *cvp; + int retval; +#endif + + switch (code) { + +#ifdef T_ATM_BUFQUEUE + case T_ATM_BUFQUEUE: + cup = (Cmn_unit *) addr; + cvp = (Cmn_vcc *) arg; + connection = idt_atm_harpconn(cup, cvp); + if (connection == NULL) + return (-1); + retval = 0; + txq = connection->queue; + if (txq == NULL) + return (-1); + for (m = txq->mget; m != NULL; m = m->m_nextpkt) + retval += m->m_pkthdr.len; + return (retval); +#endif + } + + return (ENOSYS); +} + +/******************************************************************************* + * + * Get connection pointer from Cmn_unit and Cmn_vcc + * + * in: Cmn_unit and Cmn_vcc + * out: connection (NULL=error) + * + * Date first: 05/31/2001 last: 05/31/2001 + */ + +static CONNECTION * +idt_atm_harpconn(Cmn_unit * cup, Cmn_vcc * cvp) +{ + struct vccb *vccinf; /* from HARP struct */ + IDT *idt; + int vpi; + int vci; + + idt = (IDT *) cup; + if (idt == NULL || cvp == NULL) + return (NULL); + + if (cvp->cv_connvc == NULL) + return (NULL); + + vccinf = cvp->cv_connvc->cvc_vcc; + + if (vccinf == NULL) + return (NULL); + + vpi = vccinf->vc_vpi; + vci = vccinf->vc_vci; + + return (idt_connect_find(idt, vpi, vci)); +} + +/******************************************************************************* + * + * Get CBR/VBR/UBR class from bearer attribute + * + * in: + * out: NICCBR/NICVBR/NICABR/NICUBR + * + * Date first: 06/12/2001 last: 06/13/2001 + */ + +static int +idt_atm_bearerclass(struct attr_bearer * bearer) +{ + switch (bearer->v.bearer_class) { + case T_ATM_CLASS_A:return (NICCBR); + case T_ATM_CLASS_C: + if (idt_sysctl_vbriscbr) + return (NICCBR); /* use CBR slots for VBR VC's */ + else + return (NICVBR); + case T_ATM_CLASS_X: + if (bearer->v.traffic_type == T_ATM_CBR) + return (NICCBR); + if (bearer->v.traffic_type == T_ATM_VBR) + return (NICVBR); + return (NICUBR); + } + return (NICUBR); +} + +/* The flag idt_sysctl_vbriscbr allows us to set up a CBR VC as if it were + * VBR. This is primarily to avoid cell loss at a switch that cannot seem + * to buffer one or two cells of jitter. This jitter is created when many + * CBR slots have been taken, and a new CBR VC cannot use the optimally + * spaced slots, and has to use nearby slots instead. + * + * In this case, we want to use the VC SCR as the CBR value. The PCR and MBS + * is only of interest to the switch. + * + ******************************************************************************* + * + * Initialize HARP service + * called from device attach + */ + +int +idt_harp_init(nicstar_reg_t *idt) +{ + long long tsc_val; + u_char idt_mac[6]; + int i; + int error; + + error = 0; + + /* + * Start initializing it + */ + idt->iu_unit = device_get_unit(idt->dev); + idt->iu_mtu = IDT_IFF_MTU; + idt->iu_ioctl = idt_atm_ioctl; + idt->iu_openvcc = idt_openvcc; + idt->iu_instvcc = idt_instvcc; + idt->iu_closevcc = idt_closevcc; + idt->iu_output = idt_output; + idt->iu_vcc_zone = idt_vcc_zone; + idt->iu_nif_zone = idt_nif_zone; + + /* + * Copy serial number into config space + */ + idt->iu_config.ac_serial = 0; + + idt->iu_config.ac_vendor = VENDOR_IDT; + idt->iu_config.ac_vendapi = VENDAPI_IDT_1; + idt->iu_config.ac_device = DEV_IDT_155; + idt->iu_config.ac_media = MEDIA_UNKNOWN; + idt->iu_config.ac_bustype = BUS_PCI; + + idt->iu_pif.pif_pcr = idt->cellrate_rmax; /* ATM_PCR_OC3C; */ + idt->iu_pif.pif_maxvpi = idt->conn_maxvpi; + idt->iu_pif.pif_maxvci = idt->conn_maxvci; + + snprintf(idt->iu_config.ac_hard_vers, + sizeof(idt->iu_config.ac_hard_vers), + idt->hardware); + snprintf(idt->iu_config.ac_firm_vers, + sizeof(idt->iu_config.ac_firm_vers), + IDT_VERSION); + /* + * Save device ram info for user-level programs NOTE: This really + * points to start of EEPROM and includes all the device registers + * in the lower 2 Megabytes. + */ + idt->iu_config.ac_ram = NULL; + idt->iu_config.ac_ramsize = 0; + + for (i = 0; i < 6; i++) { + idt_mac[i] = nicstar_eeprom_rd(idt, (0x6c + i)); + } + + /* looks like bad MAC */ + if ((idt_mac[3] | idt_mac[4] | idt_mac[5]) == 0) { + GET_RDTSC(tsc_val); /* 24 bits on 500mhz CPU is about + * 30msec */ + idt_mac[0] = 0x00; + idt_mac[1] = 0x20; + idt_mac[2] = 0x48; /* use Fore prefix */ + idt_mac[3] = (tsc_val >> 16) & 0xff; + idt_mac[4] = (tsc_val >> 8) & 0xff; + idt_mac[5] = (tsc_val) & 0xff; + device_printf(idt->dev, + "Cannot read MAC address from EEPROM, generating it.\n"); + } + bcopy(&idt_mac, &idt->iu_pif.pif_macaddr.ma_data, sizeof(idt_mac)); + + device_printf(idt->dev, "MAC address %6D, HWrev=%d\n", + (u_int8_t *)&idt->iu_pif.pif_macaddr.ma_data, ":", + idt->pci_rev); + + idt->iu_config.ac_macaddr = idt->iu_pif.pif_macaddr; + + /* + * Register this interface with ATM core services + */ + error = atm_physif_register(&idt->iu_cmn, IDT_DEV_NAME, idt_services); + if (error != 0) { + /* + * Registration failed - back everything out + */ + + log(LOG_ERR, "%s(): atm_physif_register failed\n", __func__); + return (error); + } + idt->iu_flags |= CUF_INITED; + +#if BSD >= 199506 + /* + * Add hook to out shutdown function at_shutdown ( + * (bootlist_fn)idt_pci_shutdown, idt, SHUTDOWN_POST_SYNC ); + */ +#endif + + return (error); +} + +/******************************************************************************* + * + * Output data + */ + +static void +idt_output(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc, KBuffer * m) +{ + struct vccb *vccinf; /* from HARP struct */ + IDT *idt; + int vpi; + int vci; + int flags; + + idt = (IDT *) cmnunit; + flags = 0; + + if (cmnvcc == NULL) { + device_printf(idt->dev, "idt_output arg error #1\n"); + goto bad; + } + if (cmnvcc->cv_connvc == NULL) { + device_printf(idt->dev, "idt_output arg error #2\n"); + goto bad; + } + vccinf = cmnvcc->cv_connvc->cvc_vcc; + if (vccinf == NULL) { + device_printf(idt->dev, "idt_output arg error #3\n"); + goto bad; + } + vpi = vccinf->vc_vpi; + vci = vccinf->vc_vci; + +#ifdef CVF_MPEG2TS /* option to split bufs into small TS bufs */ + if (cmnvcc->cv_flags & CVF_MPEG2TS) + flags = 1; +#endif + + idt_transmit(idt, m, vpi, vci, flags); + + return; +bad: + m_freem(m); + return; +} + +/******************************************************************************* + * + * Open VCC + */ + +static int +idt_openvcc(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc) +{ + Atm_attributes *attrib; /* from HARP struct */ + struct vccb *vccinf; /* from HARP struct */ + CONNECTION *connection; + IDT *idt; + int vpi; + int vci; + int class; /* NICCBR, NICVBR, or NICUBR */ + + idt = (IDT *) cmnunit; + + if (cmnvcc == NULL || cmnvcc->cv_connvc == NULL) { + printf("idt_openvcc: bad request #1.\n"); + return (1); + } + attrib = &cmnvcc->cv_connvc->cvc_attr; + vccinf = cmnvcc->cv_connvc->cvc_vcc; + + if (attrib == NULL || vccinf == NULL) { + printf("idt_openvcc: bad request #2.\n"); + return (1); + } + vpi = vccinf->vc_vpi; + vci = vccinf->vc_vci; + + connection = idt_connect_find(idt, vpi, vci); + if (connection == NULL) { + printf("idt_openvcc: vpi/vci invalid: %d/%d\n", vpi, vci); + return (1); + } + if (connection->status) { + printf("idt_openvcc: connection already open %d/%d\n", vpi, vci); + return (1); + } + connection->status = 1; + connection->recv = NULL; + connection->rlen = 0; + connection->maxpdu = 20000; + connection->aal = IDTAAL5; + connection->traf_pcr = attrib->traffic.v.forward.PCR_all_traffic; + connection->traf_scr = attrib->traffic.v.forward.SCR_all_traffic; + connection->vccinf = vccinf; /* 12/15/2000 */ + + if (connection->traf_pcr <= 0) + connection->traf_pcr = connection->traf_scr; + if (connection->traf_scr <= 0) + connection->traf_scr = connection->traf_pcr; + + class = idt_atm_bearerclass(&attrib->bearer); + if (vpi == 0 && vci == 5) + class = NICABR; /* higher priority than UBR */ + if (vpi == 0 && vci == 16) + class = NICABR; + + if (connection->traf_pcr < 0) { /* neither PCR nor SCR given */ + connection->traf_pcr = 1; + connection->traf_scr = 1; + class = NICUBR; /* so give it lowest priority */ + } + connection->class = class; + + if (idt_connect_txopen(idt, connection)) { + device_printf(idt->dev, "cannot open connection for %d/%d\n", + vpi, vci); + return (1); + } + if (idt_sysctl_logvcs) + printf("idt_openvcc: %d/%d, PCR=%d, SCR=%d\n", vpi, vci, + connection->traf_pcr, connection->traf_scr); + idt_connect_opencls(idt, connection, 1); /* open entry in rcv + * connect table */ + + return (0); +} + +/* We really don't handle ABR, but use it as a higher priority UBR. The + * idea is that a UBR connection that gives a PCR (like 0/16) should + * be given preference over a UBR connection that wants "everything else". + * + * Note that CLASS_X is typically UBR, but the traffic type information + * element may still specify CBR or VBR. + * + ******************************************************************************* + * + * Close VCC + */ + +static int +idt_closevcc(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc) +{ + CONNECTION *connection; + nicstar_reg_t *idt = (nicstar_reg_t *) cmnunit; + int vpi; + int vci; + + if (cmnvcc && cmnvcc->cv_connvc && cmnvcc->cv_connvc->cvc_vcc) { + vpi = cmnvcc->cv_connvc->cvc_vcc->vc_vpi; + vci = cmnvcc->cv_connvc->cvc_vcc->vc_vci; + } else { + printf("idt_closevcc: bad vcivpi\n"); + return (0); + } + connection = idt_connect_find(idt, vpi, vci); + + if (connection == NULL) { + printf("idt_closevcc: vpi/vci invalid: %d/%d\n", vpi, vci); + return (0); + } + idt_connect_opencls(idt, connection, 0); /* close entry in rcv + * connect table */ + + if (connection->status == 0) + printf("idt_closevcc: close on empty connection %d/%d\n", vpi, vci); + if (connection->recv != NULL) + m_freem(connection->recv); /* recycle mbuf of partial PDU */ + idt_connect_txclose(idt, connection); + connection->status = 0; + connection->recv = NULL; + connection->rlen = 0; + connection->maxpdu = 0; + connection->aal = 0; + connection->traf_pcr = 0; + connection->traf_scr = 0; + + if (idt_sysctl_logvcs) + printf("idt_closevcc: vpi=%d vci=%d\n", vpi, vci); + + return (0); +} + +/* + * + * VCC Stack Instantiation + * + * This function is called via the common driver code during a device VCC + * stack instantiation. The common code has already validated some of + * the request so we just need to check a few more IDT-specific details. + * + * Called at splnet. + * + * Arguments: + * cup pointer to device common unit + * cvp pointer to common VCC entry + * + * Returns: + * 0 instantiation successful + * err instantiation failed - reason indicated + * + */ +static int +idt_instvcc(Cmn_unit * cmnunit, Cmn_vcc * cmnvcc) +{ + Atm_attributes *attrib; /* from HARP struct */ + IDT *idt; + int class, pcr, scr; + int slots_vc, slots_cur, slots_max; + + if (cmnvcc == NULL) + return (EINVAL); + if (cmnvcc->cv_connvc == NULL) + return (EINVAL); + + idt = (IDT *) cmnunit; + if (idt == NULL) + return (EINVAL); + + attrib = &cmnvcc->cv_connvc->cvc_attr; + + if (attrib == NULL) + return (EINVAL); + + pcr = attrib->traffic.v.forward.PCR_all_traffic; + scr = attrib->traffic.v.forward.SCR_all_traffic; + + if (pcr <= 0) + pcr = scr; /* if PCR missing, default to SCR */ + if (pcr <= 0) + pcr = 1; + if (scr <= 0) + scr = pcr; + + class = idt_atm_bearerclass(&attrib->bearer); + if (class == NICCBR) { + slots_max = idt->txslots_max; + slots_cur = idt->txslots_cur; + slots_vc = idt_slots_cbr(idt, scr); /* 06/13/2001: now using + * SCR */ + if (slots_vc + slots_cur > slots_max) { + if (idt_sysctl_logvcs) + device_printf(idt->dev, + "Insufficient bandwidth (vc=%d cur=%d max=%d)\n", + slots_vc, slots_cur, slots_max); + return (EINVAL); + } + } + /* This part was take from /sys/dev/hfa/fore_vcm.c */ + + switch (attrib->aal.type) { + case ATM_AAL0: + break; + case ATM_AAL3_4: + if ((attrib->aal.v.aal4.forward_max_SDU_size > IDT_IFF_MTU) || + (attrib->aal.v.aal4.backward_max_SDU_size > IDT_IFF_MTU)) + return (EINVAL); + break; + case ATM_AAL5: + if ((attrib->aal.v.aal5.forward_max_SDU_size > IDT_IFF_MTU) || + (attrib->aal.v.aal5.backward_max_SDU_size > IDT_IFF_MTU)) + return (EINVAL); + break; + default: + return (EINVAL); + } + return (0); +} + +/* + * Pass Incoming PDU up Stack + * + * This function is called via the core ATM interrupt queue callback + * set in fore_recv_drain(). It will pass the supplied incoming + * PDU up the incoming VCC's stack. + * + * Called at splnet. + * + * Arguments: + * tok token to identify stack instantiation + * m pointer to incoming PDU buffer chain + * + * Returns: + * none + */ +static void +idt_recv_stack(void *tok, KBuffer * m) +{ + Idt_vcc *ivp = (Idt_vcc *) tok; + int err; + + if ((m->m_flags & M_PKTHDR) == 0) { + printf("idt_recv_stack: Warning - mbuf chain has no header.\n"); + KB_FREEALL(m); + return; + } + /* + * Send the data up the stack + */ + STACK_CALL(CPCS_UNITDATA_SIG, ivp->iv_upper, + ivp->iv_toku, ivp->iv_vccb, (int)m, 0, err); + if (err) + KB_FREEALL(m); + + return; +} + +/****************************************************************************** + * + * Enqueue received PDU for HARP to handle + * + * in: IDT device, mbuf, vpi, vci + * + * Date last: 12/14/2000 + */ + +void +idt_receive(nicstar_reg_t * idt, struct mbuf * m, int vpi, int vci) +{ + caddr_t cp; + Cmn_vcc *vcc; + int space; + + /* + * The STACK_CALL needs to happen at splnet() in order for the stack + * sequence processing to work. Schedule an interrupt queue + * callback at splnet() since we are currently at device level. + */ + + /* + * Prepend callback function pointer and token value to buffer. We + * have already guaranteed that the space is available in the first + * buffer. + */ + + /* + * vcc = atm_dev_vcc_find(&idt->iu_cmn, (vpivci>> 16), vpivci & + * 0xffff, VCC_IN); + */ + + vcc = atm_dev_vcc_find(&idt->iu_cmn, vpi, vci, VCC_IN); + + if (vcc == NULL) { /* harp stack not ready or no vcc */ + printf("idt_receive: no VCC %d/%d\n", vpi, vci); + KB_FREEALL(m); + return; + } + space = m->m_data - idt_mbuf_base(m); + if (space < sizeof(atm_intr_func_t) + sizeof(int)) { + printf("idt_receive: NOT enough buffer space (%d).\n", space); + KB_FREEALL(m); + return; + } + KB_HEADADJ(m, sizeof(atm_intr_func_t) + sizeof(int)); + KB_DATASTART(m, cp, caddr_t); + *((atm_intr_func_t *) cp) = idt_recv_stack; + cp += sizeof(atm_intr_func_t); + + *((void **)cp) = (void *)vcc; + + /* + * Schedule callback + */ + if (!_IF_QFULL(&atm_intrq)) { + IF_ENQUEUE(&atm_intrq, m); + schednetisr(NETISR_ATM); + } else { + KB_FREEALL(m); + } +} diff --git a/sys/dev/idt/idt_pci.c b/sys/dev/idt/idt_pci.c new file mode 100644 index 0000000..c2fe260 --- /dev/null +++ b/sys/dev/idt/idt_pci.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Matriplex, inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * This driver is derived from the Nicstar driver by Mark Tinguely, and + * some of the original driver still exists here. Those portions are... + * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely + * All rights reserved. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/socket.h> +#include <sys/socketvar.h> + +#include <sys/bus.h> +#include <sys/conf.h> + +#include <sys/module.h> +#include <machine/bus_memio.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> + +#include <netatm/port.h> +#include <netatm/queue.h> +#include <netatm/atm.h> +#include <netatm/atm_sys.h> +#include <netatm/atm_sap.h> +#include <netatm/atm_cm.h> +#include <netatm/atm_if.h> +#include <netatm/atm_stack.h> +#include <netatm/atm_pcb.h> +#include <netatm/atm_var.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/idt/idtreg.h> +#include <dev/idt/idtvar.h> + +#define IDT_VID 0x111d +#define IDT_NICSTAR_DID 0x0001 + +struct pci_type { + u_int16_t pci_vid; + u_int16_t pci_did; + char * pci_name; +} pci_devs[] = { + { IDT_VID, IDT_NICSTAR_DID, "IDT IDT77201/211 NICStAR ATM Adapter" }, + { 0, 0, NULL } +}; + +uma_zone_t idt_nif_zone; +uma_zone_t idt_vcc_zone; + +static int idt_probe (device_t); +static int idt_attach (device_t); +static int idt_detach (device_t); +static int idt_shutdown (device_t); +static void idt_free (device_t); +static int idt_modevent (module_t, int, void *); + +static int +idt_probe(device_t dev) +{ + struct pci_type *t = pci_devs; + + while(t->pci_name != NULL) { + if ((pci_get_vendor(dev) == t->pci_vid) && + (pci_get_device(dev) == t->pci_did)) { + device_set_desc(dev, t->pci_name); + return(0); + } + t++; + } + + return(ENXIO); +} + +/****************************************************************************** + * + * Attach device + * + * Date first: 11/14/2000 last: 06/10/2001 + */ + +static int +idt_attach(device_t dev) +{ + struct idt_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + error = 0; + + pci_enable_busmaster(dev); + + /* count = 2 (times 32 PCI clocks) */ + pci_write_config(dev, PCIR_LATTIMER, 0x20, 1); + + /* Map IDT registers */ + sc->mem_rid = 0x14; + sc->mem_type = SYS_RES_MEMORY; + sc->mem = bus_alloc_resource(dev, sc->mem_type, &sc->mem_rid, + 0, ~0, 1, RF_ACTIVE); + if (sc->mem == NULL) { + device_printf(dev, "could not map registers.\n"); + error = ENXIO; + goto fail; + } + sc->bustag = rman_get_bustag(sc->mem); + sc->bushandle = rman_get_bushandle(sc->mem); + + /* Map interrupt */ + sc->irq_rid = 0; + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (sc->irq == NULL) { + device_printf(dev, "could not map interrupt.\n"); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, nicstar_intr, + sc, &sc->irq_ih); + if (error) { + device_printf(dev, "could not setup irq.\n"); + error = ENXIO; + goto fail; + } + + sc->virt_baseaddr = (vm_offset_t)rman_get_virtual(sc->mem); + sc->cmd_reg = sc->virt_baseaddr + REGCMD; /* old reg */ + sc->stat_reg = sc->virt_baseaddr + REGSTAT; /* old reg */ + sc->reg_cmd = (u_long *)(sc->virt_baseaddr + REGCMD); + sc->reg_stat = (u_long *)(sc->virt_baseaddr + REGSTAT); + sc->reg_cfg = (u_long *)(sc->virt_baseaddr + REGCFG); + sc->reg_data = (u_long *)(sc->virt_baseaddr + 0); + sc->reg_tsqh = (u_long *)(sc->virt_baseaddr + REGTSQH); + sc->reg_gp = (u_long *)(sc->virt_baseaddr + REGGP); + sc->pci_rev = pci_get_revid(dev); + sc->timer_wrap = 0; + + callout_handle_init(&sc->ch); + + phys_init(sc); /* initialize the hardware */ + nicstar_init(sc); /* allocate and initialize */ + + error = idt_harp_init(sc); + if (error) + goto fail; + + return (0); +fail: + idt_free(dev); + return (error); +} + +/****************************************************************************** + * + * Detach device + * + * Date first: 11/14/2000 last: 11/14/2000 + */ + +static int +idt_detach(device_t dev) +{ + struct idt_softc *sc; + int error; + + sc = device_get_softc(dev); + error = 0; + + /* + * De-Register this interface with ATM core services + */ + error = atm_physif_deregister(&sc->iu_cmn); + + idt_device_stop(sc); /* Stop the device */ + + /* + * Lock out all device interupts. + */ + DEVICE_LOCK(&sc->iu_cmn); + idt_free(dev); + idt_release_mem(sc); + DEVICE_UNLOCK(&sc->iu_cmn); + + return (error); +} + +/****************************************************************************** + * + * Shutdown device + * + * Date first: 11/14/2000 last: 11/14/2000 + */ + +static int +idt_shutdown(device_t dev) +{ + + struct idt_softc *sc; + + sc = device_get_softc(dev); + + idt_device_stop(sc); /* Stop the device */ + + return (0); +} + +static void +idt_free (device_t dev) +{ + struct idt_softc *sc; + + sc = device_get_softc(dev); + + if (sc->irq_ih) + bus_teardown_intr(dev, sc->irq, sc->irq_ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); + if (sc->mem) + bus_release_resource(dev, sc->mem_type, sc->mem_rid, sc->mem); + + return; +} + +static int +idt_modevent (module_t mod, int what, void *arg) +{ + int error; + + error = 0; + + switch (what) { + case MOD_LOAD: + idt_nif_zone = uma_zcreate("idt nif", + sizeof(struct atm_nif), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + if (idt_nif_zone == NULL) + panic("hfa_modevent:uma_zcreate nif"); + uma_zone_set_max(idt_nif_zone, 20); + + idt_vcc_zone = uma_zcreate("idt vcc", + sizeof(Idt_vcc), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); + if (idt_vcc_zone == NULL) + panic("hfa_modevent: uma_zcreate vcc"); + uma_zone_set_max(idt_vcc_zone, 100); + + break; + + case MOD_UNLOAD: + uma_zdestroy(idt_nif_zone); + uma_zdestroy(idt_vcc_zone); + + break; + default: + break; + } + + return (error); +} + +static device_method_t idt_methods[] = { + DEVMETHOD(device_probe, idt_probe), + DEVMETHOD(device_attach, idt_attach), + DEVMETHOD(device_detach, idt_detach), + DEVMETHOD(device_shutdown, idt_shutdown), + {0, 0} +}; + +static driver_t idt_driver = { + "idt", + idt_methods, + sizeof(struct idt_softc) +}; + +static devclass_t idt_devclass; + +DRIVER_MODULE(idt, pci, idt_driver, idt_devclass, idt_modevent, 0); +MODULE_VERSION(idt, 1); diff --git a/sys/dev/idt/idtreg.h b/sys/dev/idt/idtreg.h new file mode 100644 index 0000000..43ede7e --- /dev/null +++ b/sys/dev/idt/idtreg.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * this product includes software developed by Matriplex, inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * This driver is derived from the Nicstar driver by Mark Tinguely, and + * some of the original driver still exists here. Those portions are... + * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely + * All rights reserved. + * + ****************************************************************************** + * + * $FreeBSD$ + */ + +#define IDT_VERSION "IDT 1.101" +#define CBR_VERBOSE 1 /* show CBR time slots */ + +#define IDT_MAX_CBRSLOTS 2100 /* no smaller than value assigned to card */ +#define IDT_MAX_CBRQUEUE 64 /* max number of CBR connections: 1k each */ + +/* AAL types */ +#define IDTAAL0 0 +#define IDTAAL1 1 +#define IDTAAL3_4 3 +#define IDTAAL5 5 + +#define NICCBR 1 +#define NICVBR 2 +#define NICABR 3 +#define NICUBR 4 + +/* NICStAR Operation Registers */ +#define REGCMD 0x10 /* command w */ +#define REGCFG 0x14 /* configuration r/w */ +#define REGSTAT 0x18 /* status r/w */ +#define REGRSQB 0x1c /* RSQ base w */ +#define REGRSQT 0x20 /* RSQ tail r */ +#define REGRSQH 0x24 /* RSQ head w */ +#define REGCDC 0x28 /* cell drop cnt r/c */ +#define REGVPEC 0x2c /* vci/vpi er cnt r/c */ +#define REGICC 0x30 /* invalid cell r/c */ +#define REGRAWT 0x34 /* raw cell tail r */ +#define REGTMR 0x38 /* timer r */ +#define REGTSTB 0x3c /* TST base r/w */ +#define REGTSQB 0x40 /* TSQ base w */ +#define REGTSQT 0x44 /* TSQ tail r */ +#define REGTSQH 0x48 /* TSQ head w */ +#define REGGP 0x4c /* general purp r/w */ +#define REGVMSK 0x50 /* vci/vpi mask w */ diff --git a/sys/dev/idt/idtvar.h b/sys/dev/idt/idtvar.h new file mode 100644 index 0000000..16e0c45 --- /dev/null +++ b/sys/dev/idt/idtvar.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Matriplex, inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * This driver is derived from the Nicstar driver by Mark Tinguely, and + * some of the original driver still exists here. Those portions are... + * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely + * All rights reserved. + * + ****************************************************************************** + * + * This driver supports the Fore LE155, LE25, and IDT 77211 cards. + * + * ATM CBR connections are supported, and bandwidth is allocated in + * slots of 64k each. Three VBR queues handle traffic for VBR and + * UBR. Two UBR queues prioritize UBR traffic. ILMI and signalling + * get the higher priority queue, as well as UBR traffic that specifies + * a peak cell rate. All other UBR traffic goes into the lower queue. + * + ****************************************************************************** + * + * $FreeBSD$ + */ + +/******************************************************************************* + * + * New data types + */ + +typedef struct { + struct mbuf *mget; /* head of mbuf queue, pull mbufs from here */ + struct mbuf **mput; /* tail (ptr to m_nextpkt) put mbufs here */ + u_long scd; /* segmentation channel descriptor address */ + u_long *scq_base; /* segmentation channel queue base address */ + u_long *scq_next; /* next address */ + u_long *scq_last; /* last address written */ + int scq_len; /* size of SCQ buffer (64 or 512) */ + int scq_cur; /* current number entries in SCQ buffer */ + int rate; /* cells per second allocated to this queue */ + int vbr_m; /* VBR m/n = max duty cycle for queue */ + int vbr_n; /* 1 <= m <= 7 and 1 <= n <= 127 */ +} TX_QUEUE; + +/* To avoid expensive SRAM reads, scq_cur tracks the number of SCQ entries + * in use. Only idt_transmit_top may increase this, and only idt_intr_tsq + * may decrease it. + */ + +/* mbuf chains on the queue use the fields: + * m_next is the usual pointer to next mbuf + * m_nextpkt is the next packet on the queue + * m_pkthdr.rcvif is a pointer to the connection + * m_pkthdr.header is a pointer to the TX queue + */ + +typedef struct { + struct vccb *vccinf; + char status; /* zero if closed */ + char vpi; + u_short vci; + TX_QUEUE *queue; /* transmit queue for this connection */ + struct mbuf *recv; /* current receive mbuf, or NULL */ + int rlen; /* current receive length */ + int maxpdu; /* largest PDU we will ever see */ + int traf_pcr; /* peak cell rate */ + int traf_scr; /* sustained cell rate */ + u_char aal; /* AAL for this connection */ + u_char class; /* T_ATM_CBR, T_ATM_VBR, or T_ATM_UBR */ + u_char flg_mpeg2ts:1; /* send data as 2 TS == 8 AAL5 cells */ + u_char flg_clp:1; /* CLP flag for outbound cells */ +} CONNECTION; + +#define MAX_CONNECTION 4096 /* max number of connections */ + +#define GET_RDTSC(var) {__asm__ volatile("rdtsc":"=A"(var)); } + +/******************************************************************************* + * + * Device softc structure + */ + +struct idt_softc { + /* HARP data */ + /* XXX: must be first member of struct. */ + Cmn_unit iu_cmn; /* Common unit stuff */ + +#if 0 + struct arpcom idt_ac; /* ifnet for device */ +#endif + + /* Device data */ + device_t dev; + int debug; + + struct resource * mem; + int mem_rid; + int mem_type; + bus_space_tag_t bustag; + bus_space_handle_t bushandle; + + struct resource * irq; + int irq_rid; + void * irq_ih; + + struct callout_handle ch; + + struct mtx mtx; + + vm_offset_t virt_baseaddr; /* nicstar register virtual address */ + vm_offset_t cmd_reg; /* command register offset 0x14 */ + vm_offset_t stat_reg; /* status register offset 0x60 */ + vm_offset_t fixbuf; /* buffer that holds TSQ, RSQ, variable SCQ */ + + u_long timer_wrap; /* keep track of wrapped timers */ + u_long rsqh; /* Recieve Status Queue, reg is write-only */ + + CONNECTION *connection; /* connection table */ + int conn_maxvpi; /* number of VPI values */ + int conn_maxvci; /* number of VCI values */ + int cellrate_rmax; /* max RX cells per second */ + int cellrate_tmax; /* max TX cells per second */ + int cellrate_rcur; /* current committed RX cellrate */ + int cellrate_tcur; /* current committed TX cellrate */ + int txslots_max; /* number of CBR TX slots for interface */ + int txslots_cur; /* current CBR TX slots in use */ + TX_QUEUE cbr_txqb[IDT_MAX_CBRQUEUE]; + TX_QUEUE *cbr_slot[IDT_MAX_CBRSLOTS]; + TX_QUEUE *cbr_free[IDT_MAX_CBRQUEUE]; + TX_QUEUE queue_vbr; + TX_QUEUE queue_abr; + TX_QUEUE queue_ubr; + vm_offset_t cbr_base; /* base of memory for CBR TX queues */ + int cbr_size; /* size of memory for CBR TX queues */ + int cbr_freect; + u_long raw_headp; /* head of raw cell queue, physical */ + struct mbuf *raw_headm; /* head of raw cell queue, virtual */ + u_long *tsq_base; /* virtual TSQ base address */ + u_long *tsq_head; /* virtual TSQ head pointer */ + int tsq_size; /* number of TSQ entries (1024) */ + volatile u_long *reg_cfg; + volatile u_long *reg_cmd; + volatile u_long *reg_data; + volatile u_long *reg_tsqh; + volatile u_long *reg_gp; + volatile u_long *reg_stat; + struct mbuf **mcheck; + + int sram; /* amount of SRAM */ + int pci_rev; /* hardware revision ID */ + char *hardware; /* hardware description string */ + u_char flg_le25:1; /* flag indicates LE25 instead of LE155 */ + u_char flg_igcrc:1; /* ignore receive CRC errors */ +}; + +typedef struct idt_softc nicstar_reg_t; +typedef struct idt_softc IDT; + +#define iu_pif iu_cmn.cu_pif +#define stats_ipdus iu_pif.pif_ipdus +#define stats_opdus iu_pif.pif_opdus +#define stats_ibytes iu_pif.pif_ibytes +#define stats_obytes iu_pif.pif_obytes +#define stats_ierrors iu_pif.pif_ierrors +#define stats_oerrors iu_pif.pif_oerrors +#define stats_cmderrors iu_pif.pif_cmderrors + +/* + * Device VCC Entry + * + * Contains the common and IDT-specific information for each VCC + * which is opened through a IDT device. + */ +struct nidt_vcc { + struct cmn_vcc iv_cmn; /* Common VCC stuff */ +}; + +typedef struct nidt_vcc Idt_vcc; + +extern int idt_sysctl_logvcs; +extern int idt_sysctl_vbriscbr; + +void nicstar_intr(void *); +void phys_init(nicstar_reg_t * const); +void nicstar_init(nicstar_reg_t * const); +int idt_harp_init(nicstar_reg_t * const); +void idt_device_stop(IDT *); +void idt_release_mem(IDT *); + +CONNECTION *idt_connect_find(IDT *, int, int); +caddr_t idt_mbuf_base(struct mbuf *); +int idt_slots_cbr(IDT *, int); + +int idt_connect_opencls(IDT *, CONNECTION *, int); +int idt_connect_txopen(IDT *, CONNECTION *); +int idt_connect_txclose(IDT *, CONNECTION *); + +int nicstar_eeprom_rd(nicstar_reg_t * const, u_long); + +void idt_receive(IDT *, struct mbuf *, int, int); +void idt_transmit(IDT *, struct mbuf *, int, int, int); diff --git a/sys/modules/idt/Makefile b/sys/modules/idt/Makefile new file mode 100644 index 0000000..8f1a08e --- /dev/null +++ b/sys/modules/idt/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/idt + +KMOD= idt +SRCS= idt_pci.c idt_harp.c idt.c +SRCS+= pci_if.h device_if.h bus_if.h + +.include <bsd.kmod.mk> |