/* * Copyright (c) 2000, 2001 Richard Hodges and Matriplex, inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Matriplex, inc. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** * * This driver is derived from the Nicstar driver by Mark Tinguely, and * some of the original driver still exists here. Those portions are... * Copyright (c) 1996, 1997, 1998, 1999 Mark Tinguely * All rights reserved. * ****************************************************************************** * * This driver supports the Fore LE155, LE25, and IDT 77211 cards. * * ATM CBR connections are supported, and bandwidth is allocated in * slots of 64k each. Three VBR queues handle traffic for VBR and * UBR. Two UBR queues prioritize UBR traffic. ILMI and signalling * get the higher priority queue, as well as UBR traffic that specifies * a peak cell rate. All other UBR traffic goes into the lower queue. * ****************************************************************************** * * The following sysctl variables are used: * * hw.idt.log_bufstat (0) Log free buffers (every few minutes) * hw.idt.log_vcs (0) Log VC opens, closes, and other events * hw.idt.bufs_large (100) Max/target number of free 2k buffers * hw.idt.bufs_small (200) Max/target number of free mbufs * hw.idt.cur_large (R/O) Current number of free 2k buffers * hw.idt.cur_small (R/O) Current number of free mbufs * hw.idt.qptr_hold (1) Optimize TX queue buffer for lowest overhead * * Note that the read-only buffer counts will not work with multiple cards. * ****************************************************************************** * * Assumptions: * * 1. All mbuf clusters are 2048 bytes, and aligned. * 2. All mbufs are 256 bytes, and aligned (see idt_intr_tsq). * * Bugs: * * 1. Function idt_detach() is unusuable because idt_release_mem() is * incomplete. The mbufs held in the free buffer queues can be * recovered from the "mcheck" hash table. * 2. The memory allocation could be cleaned up quite a bit. * ****************************************************************************** */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* bootverbose */ #include #include #include #include #include #include /* Gross kludge to make lint compile again. This sucks, but oh well */ #ifdef COMPILING_LINT #undef MCLBYTES #undef MCLSHIFT #define MCLBYTES 2048 #define MCLSHIFT 11 #endif #if MCLBYTES != 2048 #error "This nicstar driver depends on 2048 byte mbuf clusters." #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 == 0) return; /* no space card disabled */ if (idt_buffer_init(idt)) /* allocate large buffers */ goto freemem; /* free memory and return */ if (idt_mcheck_init(idt)) goto freemem; idt_found++; /* number of cards found on machine */ if (bootverbose) { printf("nicstar: buffer size %d\n", 0); } idt_queue_init(idt); /* initialize all TX_QUEUE structures */ idt_slots_init(idt); /* initialize CBR table slots */ /* initialize variable rate mbuf queues */ 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 != 0) kmem_free(kernel_map, idt->fixbuf, (NICSTAR_FIXPAGES * PAGE_SIZE)); if (idt->cbr_base != 0) kmem_free(kernel_map, (vm_offset_t)idt->cbr_base, idt->cbr_size); printf("%s() is NOT SAFE!\n", __func__); /* we also have idt->connection and idt->mcheck to do as well... */ } /****************************************************************************** * * Write one to four words to SRAM * * writes one to four words into sram starting at "sram_location" * * returns -1 if sram location is out of range. * returns count, if count is not in the range from 1-4. * returns 0 if parameters were acceptable */ static int nicstar_sram_wr(nicstar_reg_t * const idt, u_long address, int count, u_long data0, u_long data1, u_long data2, u_long data3) { if (address >= 0x20000) /* bad address */ return (-1); if (idt_status_wait(idt)) /* 12/06/2000 */ return (-1); switch (--count) { case 3: *(idt->reg_data + 3) = data3; /* drop down to do others */ case 2: *(idt->reg_data + 2) = data2; /* drop down to do others */ case 1: *(idt->reg_data + 1) = data1; /* drop down to do others */ case 0: *idt->reg_data = data0; /* load last data item */ break; /* done loading values */ default: return (count); /* nothing to do */ } /* write the word(s) */ *idt->reg_cmd = 0x40000000 | (address << 2) | count; return (0); } /* 05/31/2001: Removed wait between data register(s) and write command. * The docs do not state it is helpful, and the example only has one * wait, before the data register load. The wait time is very high - * aproximately 6 microseconds per wait. * ****************************************************************************** * * Read one word from SRAM * * reads one word of sram at "sram_location" and places the value * in "answer_pointer" * * returns -1 if sram location is out of range. * returns 0 if parameters were acceptable */ static int nicstar_sram_rd(nicstar_reg_t * const idt, u_long address, u_long *data0) { if (address >= 0x20000) /* bad address */ return (-1); if (idt_status_wait(idt)) return (-1); *idt->reg_cmd = 0x50000000 | (address << 2); /* read a word */ if (idt_status_wait(idt)) return (-1); *data0 = *idt->reg_data;/* save word */ return (0); } /******************************************************************************* * * Open or Close connection in IDT Receive Connection Table * * in: IDT device, VPI, VCI, opflag (0 = close, 1 = open) * out: zero = success * * Date first: 12/14/2000 last: 12/14/2000 */ int idt_connect_opencls(IDT * idt, CONNECTION * connection, int opflag) { int address; int word1; if (connection->vpi >= idt->conn_maxvpi || connection->vci >= idt->conn_maxvci) return (1); address = connection->vpi * idt->conn_maxvci + connection->vci; address <<= 2; /* each entry is 4 words */ if (opflag) { switch (connection->aal) { case ATM_AAL0: word1 = 0x00038000; break; /* raw cell queue */ case ATM_AAL1: word1 = 0x00008000; break; /* Nicstar "AAL0" */ case ATM_AAL3_4: word1 = 0x00018000; break; case ATM_AAL5: word1 = 0x00028000; break; default: return (1); } nicstar_sram_wr(idt, address, 4, word1, 0, 0, 0xffffffff); opflag = 0x00080000; /* bit-19 set or clear */ } if (idt_status_wait(idt)) return (1); *idt->reg_cmd = 0x20000000 | opflag | address << 2; return (0); } /******************************************************************************* * * nicstar_add_buf ( card, mbuf1, mbuf2, which_queue) * * This adds two buffers to the specified queue. This uses the * mbuf address as handle and the buffer physical address must be * the DMA address. * * returns -1 if queue is full, the address is not word aligned, or * an invalid queue is specified. * returns 0 if parameters were acceptable. */ int nicstar_add_buf(nicstar_reg_t * const idt, struct mbuf * buf0, struct mbuf * buf1, u_long islrg) { u_long stat_val; u_long val0, val1, val2, val3; if (islrg > 1) /* bad buffer size */ return (-1); stat_val = *idt->reg_stat; if (islrg) { if (stat_val & 0x80) /* large queue is full */ return (-1); } else if (stat_val & 0x100) /* small queue is full */ return (-1); if (!buf0 || !buf1 || ((u_long)(buf0->m_data) & 0x7) || ((u_long)(buf1->m_data) & 0x7)) { return (-1); /* buffers must word aligned */ } if (idt->raw_headm == NULL) /* raw cell buffer pointer not * initialized */ if (islrg) { idt->raw_headm = buf0; idt->raw_headp = vtophys(buf0->m_data); } if (idt_status_wait(idt)) /* 12/06/2000 */ return (-1); val0 = (u_long)buf0; /* mbuf address is handle */ val1 = vtophys(buf0->m_data); /* DMA addr of buff1 */ val2 = (u_long)buf1; /* mbuf address is handle */ val3 = vtophys(buf1->m_data); /* DMA addr of buff2 */ *(idt->reg_data + 0) = val0; *(idt->reg_data + 1) = val1; *(idt->reg_data + 2) = val2; *(idt->reg_data + 3) = val3; *idt->reg_cmd = 0x60000000 | islrg; idt_mcheck_add(idt, buf0); idt_mcheck_add(idt, buf1); return (0); } /****************************************************************************** * * nicstar_util_rd ( card, util_location, answer_pointer ) * * reads one byte from the utility bus at "util_location" and places the * value in "answer_pointer" * * returns -1 if util location is out of range. * returns 0 if parameters were acceptable */ static int nicstar_util_rd(nicstar_reg_t * const idt, u_long address, u_long *data) { if (address >= 0x81) /* bad address */ return (-1); if (idt_status_wait(idt)) return (-1); *idt->reg_cmd = 0x80000200 | address; /* read a word */ if (idt_status_wait(idt)) return (-1); *data = *idt->reg_data & 0xff; /* save word */ return (0); } /****************************************************************************** * * nicstar_util_wr ( card, util location, data ) * * writes one byte to the utility bus at "util_location" * * returns -1 if util location is out of range. * returns 0 if parameters were acceptable */ static int nicstar_util_wr(nicstar_reg_t * const idt, int cs, u_long address, u_long data) { if (address >= 0x81) /* bad address */ return (-1); if (cs > 1) return (-1); if (idt_status_wait(idt)) return (-1); *idt->reg_data = data & 0xff; /* load last data item */ if (cs == 0) *idt->reg_cmd = 0x90000100 | address; /* write the byte, CS1 */ else *idt->reg_cmd = 0x90000200 | address; /* write the byte, CS2 */ return (0); } /****************************************************************************** * * nicstar_eeprom_rd ( card , byte_location ) * * reads one byte from the utility bus at "byte_location" and return the * value as an integer. this routint is only used to read the MAC address * from the EEPROM at boot time. */ int nicstar_eeprom_rd(nicstar_reg_t * const idt, u_long address) { volatile u_long *regGP = (volatile u_long *)(idt->virt_baseaddr + REGGP); volatile u_long gp = *regGP & 0xfffffff0; int i, value = 0; DELAY(5); /* make sure idle */ *regGP = gp | 0x06; /* CS and Clock high */ DELAY(5); *regGP = gp; /* CS and Clock low */ DELAY(5); /* toggle in READ CMD (00000011) */ *regGP = gp | 0x04; /* Clock high (data 0) */ DELAY(5); *regGP = gp; /* CS and Clock low */ DELAY(5); *regGP = gp | 0x04; /* Clock high (data 0) */ DELAY(5); *regGP = gp; /* CS and Clock low */ DELAY(5); *regGP = gp | 0x04; /* Clock high (data 0) */ DELAY(5); *regGP = gp; /* CS and Clock low */ DELAY(5); *regGP = gp | 0x04; /* Clock high (data 0) */ DELAY(5); *regGP = gp; /* CS and Clock low */ DELAY(5); *regGP = gp | 0x04; /* Clock high (data 0) */ DELAY(5); *regGP = gp; /* CS and Clock low */ DELAY(5); *regGP = gp | 0x04; /* Clock high (data 0) */ DELAY(5); *regGP = gp | 0x01; /* CS and Clock low data 1 */ DELAY(5); *regGP = gp | 0x05; /* Clock high (data 1) */ DELAY(5); *regGP = gp | 0x01; /* CS and Clock low data 1 */ DELAY(5); *regGP = gp | 0x05; /* Clock high (data 1) */ DELAY(5); /* toggle in the address */ for (i = 7; i >= 0; i--) { *regGP = (gp | ((address >> i) & 1)); /* Clock low */ DELAY(5); *regGP = (gp | 0x04 | ((address >> i) & 1)); /* Clock high */ DELAY(5); } /* read EEPROM data */ for (i = 7; i >= 0; i--) { *regGP = gp; /* Clock low */ DELAY(5); value |= ((*regGP & 0x10000) >> (16 - i)); *regGP = gp | 0x04; /* Clock high */ DELAY(5); } *regGP = gp; /* CS and Clock low */ return (value); } /******************************************************************************* * * Load the card receive buffers * * in: IDT device * * Date first: 11/01/2000 last: 05/25/2000 */ void nicstar_ld_rcv_buf(IDT * idt) { struct mbuf *m1, *m2; u_long stat_reg; int card_small; int card_large; int s; s = splimp(); stat_reg = *(volatile u_long *)idt->stat_reg; card_small = (stat_reg & 0xff000000) >> 23; /* reg is number of * pairs */ card_large = (stat_reg & 0x00ff0000) >> 15; if (idt_sysctl_bufsmall > 510) idt_sysctl_bufsmall = 510; if (idt_sysctl_buflarge > 510) idt_sysctl_buflarge = 510; if (idt_sysctl_bufsmall < 10) idt_sysctl_bufsmall = 10; if (idt_sysctl_buflarge < 10) idt_sysctl_buflarge = 10; while (card_small < idt_sysctl_bufsmall) { /* 05/25/2001 from fixed */ MGETHDR(m1, M_DONTWAIT, MT_DATA); if (m1 == NULL) break; MGETHDR(m2, M_DONTWAIT, MT_DATA); if (m2 == NULL) { m_free(m1); break; } MH_ALIGN(m1, 96); /* word align & allow lots of * prepending */ MH_ALIGN(m2, 96); if (nicstar_add_buf(idt, m1, m2, 0)) { device_printf(idt->dev, "Cannot add small buffers, size=%d.\n", card_small); m_free(m1); m_free(m2); break; } card_small += 2; } while (card_large < idt_sysctl_buflarge) { /* 05/25/2001 from fixed */ m1 = idt_mbufcl_get(); if (m1 == NULL) break; m2 = idt_mbufcl_get(); if (m2 == NULL) { m_free(m1); break; } if (nicstar_add_buf(idt, m1, m2, 1)) { device_printf(idt->dev, "Cannot add large buffers, size=%d.\n", card_large); m_free(m1); m_free(m2); break; } card_large += 2; } idt_sysctl_curlarge = card_large; idt_sysctl_cursmall = card_small; splx(s); } /******************************************************************************* * * Wait for command to finish * * in: IDT device * out: zero = success * * Date first: 12/06/2000 last: 12/16/2000 */ int idt_status_wait(IDT * idt) { int timeout; timeout = 33 * 100; /* allow 100 microseconds timeout */ while (*idt->reg_stat & 0x200) if (--timeout == 0) { device_printf(idt->dev, "timeout waiting for device status.\n"); idt->stats_cmderrors++; return (1); } return (0); } /******************************************************************************* * * Log status of system buffers * * in: IDT device * * Date first: 10/31/2000 last: 05/25/2001 */ void idt_status_bufs(IDT * idt) { u_long stat_reg; int card_small; int card_large; int s; s = splimp(); stat_reg = *(volatile u_long *)idt->stat_reg; card_small = (stat_reg & 0xff000000) >> 23; /* reg is number of * pairs */ card_large = (stat_reg & 0x00ff0000) >> 15; splx(s); device_printf(idt->dev, "BUFFER STATUS: small=%d/%d, large=%d/%d.\n", card_small, idt_sysctl_bufsmall, card_large, idt_sysctl_buflarge); } /* Since this is called when the card timer wraps, we should only see * this 16 times (LE155) or 10 (LE25) per hour. * ******************************************************************************* * * Add mbuf into "owned" list * * in: IDT device, mbuf * out: zero = success * * Date first: 11/13/2000 last: 11/13/2000 */ int idt_mcheck_add(IDT * idt, struct mbuf * m) { int hpos; int s; hpos = (((int)m) >> 8) & 1023; s = splimp(); m->m_next = idt->mcheck[hpos]; idt->mcheck[hpos] = m; splx(s); return (0); } /****************************************************************************** * * Remove mbuf from "owned" list * * in: IDT device, mbuf * out: zero = success * * Date first: 11/13/2000 last: 11/13/2000 */ int idt_mcheck_rem(IDT * idt, struct mbuf * m) { struct mbuf *nbuf; int hpos; int s; hpos = (((int)m) >> 8) & 1023; s = splimp(); nbuf = idt->mcheck[hpos]; if (nbuf == m) { idt->mcheck[hpos] = m->m_next; splx(s); m->m_next = NULL; return (0); } while (nbuf != NULL) { if (nbuf->m_next != m) { nbuf = nbuf->m_next; continue; } nbuf->m_next = m->m_next; splx(s); m->m_next = NULL; return (0); } splx(s); device_printf(idt->dev, "Card should not have this mbuf! %x\n", (int)m); return (1); } /****************************************************************************** * * Initialize mbuf "owned" list * * in: IDT device * out: zero = success * * Date first: 11/13/2000 last: 05/26/2001 */ int idt_mcheck_init(IDT * idt) { int size; int x; size = round_page(sizeof(struct mbuf *) * 1024); idt->mcheck = (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 == 0) return (1); idt->cbr_freect = idt->cbr_size / (16 * 64); for (x = 0; x < idt->cbr_freect; x++) { txqueue = &idt->cbr_txqb[x]; txqueue->mget = NULL; txqueue->mput = NULL; txqueue->scd = IDT_SCD_START + x * 12; txqueue->scq_base = (u_long *)scqbase; txqueue->scq_next = txqueue->scq_base; txqueue->scq_last = txqueue->scq_next; txqueue->scq_len = 64; /* all CBR queues use 64 entries */ txqueue->scq_cur = 0; txqueue->rate = 0; txqueue->vbr_m = 0; /* m & n set to zero for CBR */ txqueue->vbr_n = 0; idt->cbr_free[x] = txqueue; scqbase += 64 * 16; nicstar_sram_wr(idt, txqueue->scd, 4, vtophys(txqueue->scq_base), 0, 0xffffffff, 0); } txqueue = &idt->queue_vbr; /* VBR queue */ txqueue->mget = NULL; txqueue->mput = NULL; txqueue->scd = 0x1e7f4; txqueue->scq_base = (u_long *)(idt->fixbuf + 0x4000); txqueue->scq_next = txqueue->scq_base; txqueue->scq_last = txqueue->scq_next; txqueue->scq_len = 512; /* all VBR queues use 512 entries */ txqueue->scq_cur = 0; txqueue->rate = 0; txqueue->vbr_m = 1; txqueue->vbr_n = 1; nicstar_sram_wr(idt, txqueue->scd, 4, vtophys(txqueue->scq_base), 0, 0xffffffff, 0); txqueue = &idt->queue_abr; /* ABR queue (not currently used) */ txqueue->mget = NULL; txqueue->mput = NULL; txqueue->scd = 0x1e7e8; txqueue->scq_base = (u_long *)(idt->fixbuf + 0x6000); txqueue->scq_next = txqueue->scq_base; txqueue->scq_last = txqueue->scq_next; txqueue->scq_len = 512; txqueue->scq_cur = 0; txqueue->rate = 0; txqueue->vbr_m = 1; txqueue->vbr_n = 1; nicstar_sram_wr(idt, txqueue->scd, 4, vtophys(txqueue->scq_base), 0, 0xffffffff, 0); txqueue = &idt->queue_ubr; /* UBR queue */ txqueue->mget = NULL; txqueue->mput = NULL; txqueue->scd = 0x1e7dc; txqueue->scq_base = (u_long *)(idt->fixbuf + 0x8000); txqueue->scq_next = txqueue->scq_base; txqueue->scq_last = txqueue->scq_next; txqueue->scq_len = 512; txqueue->scq_cur = 0; txqueue->rate = 0; txqueue->vbr_m = 1; /* since the ABR queue is lowest priority, */ txqueue->vbr_n = 1; /* these factors should never change */ nicstar_sram_wr(idt, txqueue->scd, 4, vtophys(txqueue->scq_base), 0, 0xffffffff, 0); return (0); } /******************************************************************************* * * Get mbuf chain from TX_QUEUE * * in: CONNECTION * out: mbuf, NULL = empty * * Date first: 12/03/2000 last: 12/03/2000 */ static struct mbuf * idt_queue_get(TX_QUEUE * txqueue) { struct mbuf *m1, *m2; int s; if (txqueue == NULL) return (NULL); s = splimp(); m1 = txqueue->mget; if (m1 != NULL) { m2 = m1->m_nextpkt; txqueue->mget = m2; if (m2 == NULL) /* is queue empty now? */ txqueue->mput = NULL; } splx(s); return (m1); } /******************************************************************************* * * Add mbuf chain to connection TX_QUEUE * * in: CONNECTION, mbuf chain * out: zero = succes * * Date first: 12/03/2000 last: 06/01/2001 */ static int idt_queue_put(CONNECTION * connection, struct mbuf * m) { TX_QUEUE *txqueue; int s; if (connection == NULL) { m_freem(m); return (1); } txqueue = connection->queue; if (txqueue == NULL) { m_freem(m); return (1); } m->m_nextpkt = NULL; m->m_pkthdr.rcvif = (struct ifnet *) connection; s = splimp(); if (txqueue->mput != NULL) { *txqueue->mput = m; txqueue->mput = &m->m_nextpkt; } else { /* queue is empty */ txqueue->mget = m; txqueue->mput = &m->m_nextpkt; } splx(s); return (0); } /******************************************************************************* * * Flush all connection mbufs from TX_QUEUE * * in: CONNECTION * out: zero = succes * * Date first: 12/03/2000 last: 12/03/2000 */ static int idt_queue_flush(CONNECTION * connection) { TX_QUEUE *txqueue; struct mbuf **m0, *m1; int s; if (connection == NULL) return (1); txqueue = connection->queue; if (txqueue == NULL) return (1); s = splimp(); m0 = &txqueue->mget; m1 = *m0; while (m1 != NULL) { if (m1->m_pkthdr.rcvif == (struct ifnet *) connection) { *m0 = m1->m_nextpkt; m_freem(m1); m1 = *m0; continue; } m0 = &m1->m_nextpkt; m1 = *m0; } txqueue->mput = m0; splx(s); return (0); } /******************************************************************************* * * Calculate number of table positions for CBR connection * * in: IDT device, PCR (cells/second) * out: table positions needed (minimum = 1) * * Date first: 11/29/2000 last: 06/12/2001 */ int idt_slots_cbr(IDT * idt, int pcr) { unsigned int bitrate; unsigned int slots; unsigned int rem; if (pcr == 171) { if (idt_sysctl_logvcs) device_printf(idt->dev, "idt_slots_cbr: CBR channel=64000, 1 slot\n"); return (1); } if (pcr < 171) { if (idt_sysctl_logvcs) device_printf(idt->dev, "idt_slots_cbr: CBR pcr %d rounded up to 1 slot\n", pcr); return (1); } bitrate = pcr * 47 * 8; slots = bitrate / 64000; rem = bitrate % 64000; if (rem && idt_sysctl_logvcs) device_printf(idt->dev, "idt_slots_cbr: CBR cell rate rounded down to %d from %d\n", ((slots * 64000) / 376), pcr); /* slots++; */ if (idt_sysctl_logvcs) device_printf(idt->dev, "idt_slots_cbr: CBR pcr=%d, slots=%d.\n", pcr, slots); return (slots); } /* The original algorithm rounded up or down by 32k, the goal being to * map 64000 requests exactly. Unfortunately, this caused one particular * SVC to be set one slot too low, causing mbuf cluster starvation. * We can still handle the single 64k channel with a special case, and * let all others fall where they may. * ******************************************************************************* * * Add TX QUEUE pointer to slots in CBR table * * in: IDT device, TX_QUEUE, number slots * out: zero = success * * Date first: 11/29/2000 last: 06/11/2001 */ static int idt_slots_add(IDT * idt, TX_QUEUE * queue, int slots) { TX_QUEUE *curval; int p_max; /* extra precision slots maximum */ int p_spc; /* extra precision spacing value */ int p_ptr; /* extra precision pointer */ int qptr, qmax; int qlast; int scdval; if (slots < 1) return (1); qmax = idt->txslots_max; p_max = qmax << 8; p_spc = p_max / slots; p_ptr = p_spc >> 1; /* use half spacing for start point */ qptr = p_ptr >> 8; qlast = qptr; scdval = 0x20000000 | queue->scd; if (CBR_VERBOSE) { printf("idt_slots_add: p_max = %d\n", p_max); printf("idt_slots_add: p_spc = %d\n", p_spc); printf("idt_slots_add: p_ptr = %d\n", p_ptr); printf("idt_slots_add: qptr = %d\n", qptr); } while (slots) { if (qptr >= qmax) /* handle wrap for empty slot choosing */ qptr -= qmax; curval = idt->cbr_slot[qptr]; if (curval != NULL) { /* this slot has CBR, so try next */ qptr++; /* next slot */ continue; } if (CBR_VERBOSE) { printf("idt_slots_add: using qptr %d (%d)\n", qptr, qptr - qlast); qlast = qptr; } idt->cbr_slot[qptr] = queue; nicstar_sram_wr(idt, qptr + IDT_TST_START, 1, scdval, 0, 0, 0); slots--; p_ptr += p_spc; if (p_ptr >= p_max) /* main pointer wrap */ p_ptr -= p_max; qptr = p_ptr >> 8; } return (0); } /* 06/11/2001: Extra precision pointer is used in order to handle cases where * fractional slot spacing causes a large area of slots to be filled. * This can cause further CBR circuits to get slots that have very * poor spacing. * ******************************************************************************* * * Remove TX QUEUE pointer from slots in CBR table * * in: IDT device, TX_QUEUE * out: number of CBR slots released * * Date first: 12/03/2000 last: 12/03/2000 */ static int idt_slots_rem(IDT * idt, TX_QUEUE * queue) { int qptr, qmax; int slots; qmax = idt->txslots_max; slots = 0; for (qptr = 0; qptr < qmax; qptr++) { if (idt->cbr_slot[qptr] != queue) continue; idt->cbr_slot[qptr] = NULL; nicstar_sram_wr(idt, qptr + IDT_TST_START, 1, 0x40000000, 0, 0, 0); slots++; } return (slots); } /******************************************************************************* * * Initialize slots in CBR table * * in: IDT device * out: zero = success * * Date first: 11/29/2000 last: 11/29/2000 */ static int idt_slots_init(IDT * idt) { int start; /* table start pointer */ int qptr; start = IDT_TST_START; /* first, fill up the TX CBR table with 'VBR' entries */ for (qptr = 0; qptr < idt->txslots_max; qptr++) { idt->cbr_slot[qptr] = NULL; nicstar_sram_wr(idt, qptr + start, 1, 0x40000000, 0, 0, 0); } /* now write the jump back to the table start */ nicstar_sram_wr(idt, qptr + start, 1, 0x60000000 | start, 0, 0, 0); return (0); } /******************************************************************************* * * Open output queue for connection * * in: IDT device, connection (class, traf_pcr, & traf_scr fields valid) * out: zero = success * * Date first: 11/29/2000 last: 06/13/2001 */ int idt_connect_txopen(IDT * idt, CONNECTION * connection) { TX_QUEUE *txqueue; int cellrate; int cbr_slots; int s; cellrate = connection->traf_scr; /* 06/13/2001 use SCR instead * of PCR */ if (connection->class == T_ATM_UBR) { /* UBR takes whatever is left * over */ connection->queue = &idt->queue_ubr; if (idt_sysctl_logvcs) printf("idt_connect_txopen: UBR connection for %d/%d\n", connection->vpi, connection->vci); return (0); } if (connection->class == T_ATM_ABR) { /* ABR treated as UBR-plus */ connection->queue = &idt->queue_abr; if (idt_sysctl_logvcs) printf("idt_connect_txopen: UBR+ connection for %d/%d\n", connection->vpi, connection->vci); return (0); } if (connection->class == T_ATM_CBR) { cbr_slots = idt_slots_cbr(idt, cellrate); s = splimp(); if (cbr_slots > (idt->txslots_max - idt->txslots_cur) || idt->cbr_freect < 1) { splx(s); return (1); /* requested rate not available */ } idt->txslots_cur += cbr_slots; idt->cellrate_tcur += cellrate; idt->cbr_freect--; txqueue = idt->cbr_free[idt->cbr_freect]; txqueue->rate = cellrate; /* was connection->traf_pcr */ if (idt_slots_add(idt, txqueue, cbr_slots)) { idt->txslots_cur -= cbr_slots; /* cannot add CBR slots */ idt->cellrate_tcur -= cellrate; idt->cbr_free[idt->cbr_freect] = txqueue; idt->cbr_freect++; splx(s); return (1); } splx(s); if (idt_sysctl_logvcs) printf("idt_connect_txopen: CBR connection for %d/%d\n", connection->vpi, connection->vci); connection->queue = txqueue; } if (connection->class == T_ATM_VBR) { txqueue = &idt->queue_vbr; connection->queue = txqueue; txqueue->rate += connection->traf_scr; /* from traf_pcr * 12/17/2000 */ if (idt_sysctl_logvcs) printf("idt_connect_txopen: VBR connection for %d/%d\n", connection->vpi, connection->vci); } idt_connect_newvbr(idt);/* recalculate VBR divisor values */ if (connection->class == T_ATM_CBR || connection->class == T_ATM_VBR) return (0); return (1); /* unknown class */ } /******************************************************************************* * * Close connection output queue * * in: IDT device, connection (class, traf_pcr, & traf_scr fields valid) * out: zero = success * * Date first: 12/03/2000 last: 12/03/2000 */ int idt_connect_txclose(IDT * idt, CONNECTION * connection) { TX_QUEUE *txqueue; int cellrate; int slots; int s; cellrate = connection->traf_pcr; txqueue = connection->queue; if (idt_sysctl_logvcs) printf("idt_connect_txclose: closing connection for %d/%d\n", connection->vpi, connection->vci); idt_queue_flush(connection); /* flush all connection mbufs */ if (connection->class == T_ATM_UBR || /* UBR takes whatever is left * over */ connection->class == T_ATM_ABR) { /* ABR not supported, use UBR */ connection->queue = NULL; return (0); } if (connection->class == T_ATM_CBR) { slots = idt_slots_rem(idt, txqueue); /* remove this queue * from CBR slots */ s = splimp(); idt->txslots_cur -= slots; idt->cellrate_tcur -= cellrate; if (txqueue != NULL) { /* 06/12/2001 check for failure on * open */ idt->cbr_free[idt->cbr_freect] = txqueue; idt->cbr_freect++; } splx(s); connection->queue = NULL; } if (connection->class == T_ATM_VBR) { txqueue = &idt->queue_vbr; connection->queue = NULL; txqueue->rate -= connection->traf_scr; /* from traf_pcr * 12/17/2000 */ } idt_connect_newvbr(idt);/* recalculate VBR divisor values */ if (connection->class == T_ATM_CBR || connection->class == T_ATM_VBR) return (0); return (1); /* unknown class */ } /******************************************************************************* * * Calculate new VBR divisor values * * in: IDT device * * Date first: 12/03/2000 last: 12/03/2000 */ static void idt_connect_newvbr(IDT * idt) { TX_QUEUE *txqueue; int rate_newvbr; int rate_noncbr; int divisor; txqueue = &idt->queue_vbr; rate_newvbr = txqueue->rate; rate_noncbr = idt->cellrate_tmax - idt->cellrate_tcur; if (rate_newvbr < 1) /* keep sane and prevent divide by zero */ rate_newvbr = 1; if (rate_newvbr >= rate_noncbr) { txqueue->vbr_m = 1; txqueue->vbr_n = 1; return; } divisor = rate_newvbr * 1000; /* size of lookup table */ divisor += rate_newvbr >> 1; /* apply rounding to divide */ divisor /= rate_noncbr; /* always < 1000, since newvbr < noncbr */ if (idt_sysctl_logvcs) printf("idt_connect_newvbr: divisor=%d\n", divisor); txqueue->vbr_m = vbr_div_m[divisor]; txqueue->vbr_n = vbr_div_n[divisor]; if (idt_sysctl_logvcs) printf("idt_connect_newvbr: m=%d, n=%d\n", txqueue->vbr_m, txqueue->vbr_n); } /* For VBR, we track the sum of all the VBR peak cellrates, and divide * that from the "remaining" bandwidth, which is total minus current CBR. * * We will need to adjust the VBR divisor whenever we add a CBR or VBR. * * Because of the integer scalign (1000) preload, the cellrate for the * VBR channel should not exceed 2 million (aprox 5 OC3s). This is * protected by the check for rate_newvbr >= rate_noncbr. * ******************************************************************************* * * Initialize large buffers, indexes, and reference counts * * in: IDT device * out: zero = success * * Date first: 11/01/2000 last: 05/25/2001 */ int idt_buffer_init(IDT * idt) { idt->raw_headm = NULL; /* nicstar_add_buf() will initialize */ idt->raw_headp = 0; return (0); } /******************************************************************************* * * Get large buffer from kernel pool * * out: mbuf, NULL = error * * Date first: 05/25/2001 last: 05/25/2001 */ struct mbuf * idt_mbufcl_get(void) { struct mbuf *m; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return (NULL); MCLGET(m, M_DONTWAIT); if (m->m_flags & M_EXT) return (m); m_freem(m); return (NULL); } /******************************************************************************* * * Initialize connection table * * in: IDT, number of VPI bits (0, 1, or 2) * out: zero = success * * Date first: 10/29/2000 last: 12/10/2000 */ int idt_connect_init(IDT * idt, int vpibits) { CONNECTION *connection; int pages; int vpi; int vci; switch (vpibits) { case 1: idt->conn_maxvpi = 2; idt->conn_maxvci = 2048; break; case 2: idt->conn_maxvpi = 4; idt->conn_maxvci = 1024; break; default: idt->conn_maxvpi = 1; idt->conn_maxvci = 4096; } pages = (sizeof(CONNECTION) * MAX_CONNECTION) + PAGE_SIZE - 1; pages /= PAGE_SIZE; idt->connection = (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 * ******************************************************************************* * * Drop transmit mbuf chain and update counters * * in: IDT device, mbuf chain * out: (nothing) * * Date first: 11/08/2000 last: 11/08/2000 */ void idt_transmit_drop(IDT * idt, struct mbuf * mfirst) { struct mbuf *next; int mesglen; mesglen = 0; while (mfirst != NULL) { mesglen += mfirst->m_len; next = m_free(mfirst); mfirst = next; } device_printf(idt->dev, "dropping transmit packet, size=%d\n", mesglen); idt->stats_oerrors++; /* 12/15/2000 */ } /******************************************************************************* * * Put mbuf chain on transmit queue * * in: IDT device, TX_QUEUE * out: (nothing) * * Date first: 12/03/2000 last: 06/01/2001 */ void idt_transmit_top(IDT * idt, TX_QUEUE * txqueue) { CONNECTION *connection; struct mbuf *top, *m; static int padding[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int scq_space; int val, val1, val3, val4; int count, mlen, tlen, pad; char *mptr; int pdulen; int vci, vpi; int s; if (txqueue == NULL) /* 12/12/2000 */ return; if (txqueue->mget == NULL) /* check for empty queue */ return; s = splimp(); scq_space = txqueue->scq_len - txqueue->scq_cur; /* Now we can add the queue entries for the PDUs */ count = 0; for (;;) { top = txqueue->mget; /* next available mbuf */ if (top == NULL) break; if (top->m_pkthdr.csum_data + 4 > scq_space) break; /* not enough space for this PDU */ top = idt_queue_get(txqueue); if (top == NULL) break; connection = (CONNECTION *) top->m_pkthdr.rcvif; vpi = connection->vpi; vci = connection->vci; top->m_pkthdr.header = (void *)connection->queue; top->m_pkthdr.csum_data = 0; /* track actual number of SCQ * entries used */ tlen = top->m_pkthdr.len; switch (connection->aal) { case ATM_AAL0: val = 0; break; case ATM_AAL3_4: val = 0x04000000; break; case ATM_AAL5: val = 0x08000000; break; default: device_printf(idt->dev, "bad AAL for %d/%d\n", vpi, vci); m_freem(top); connection->vccinf->vc_oerrors++; continue; } val |= txqueue->vbr_m << 23; val |= txqueue->vbr_n << 16; val4 = (vpi << 20) | (vci << 4); if (connection->flg_clp) val4 |= 1; /* set CLP flag */ /* * Now we are ready to start mapping the mbuf(s) to transmit * buffer descriptors. If the MPEG2TS flag is set, we want * to create AAL5 PDUs of exactly 384 data bytes each. */ pdulen = top->m_pkthdr.len; /* default case: don't split * PDU */ pad = 0; if (connection->flg_mpeg2ts) { if ((pdulen % 376) == 0) { /* correct multiple */ pdulen = 376; /* cut off every pdu at 374 * data bytes */ pad = 8; } else device_printf(idt->dev, "Bad MPEG2 PDU buffer (%d bytes).\n", pdulen); } val3 = pdulen; /* actual (unpadded) PDU length */ pdulen += (4 - (pdulen & 3)) & 3; if (pad == 0) { /* normal padding (PDU not split) */ pad = pdulen; if (connection->aal == ATM_AAL5) pad += 8; pad = 48 - (pad % 48); if (pad == 48) pad = 0; if (connection->aal == ATM_AAL5) pad += 8; /* pad of up to 52 is * possible/neccessary */ } tlen = 0; for (m = top; m != NULL; m = m->m_next) { while ((mlen = m->m_len)) { if (mlen + tlen > pdulen) mlen = pdulen - tlen; /* how much of this * buffer can we use? */ mptr = m->m_data; tlen += mlen; /* length of this PDU */ m->m_len -= mlen; /* bytes remaining in * mbuf */ m->m_data += mlen; /* new data pointer */ val1 = val; if (tlen > pdulen + pad - 48) /* is this buffer in the * last cell? */ val1 |= 0x40000000; /* last buffer in PDU */ if (tlen == pdulen) { /* end of PDU, so figure * padding needed */ idt->stats_opdus++; /* 12/15/2000 */ idt->stats_obytes += pdulen; /* 12/15/2000 */ connection->vccinf->vc_opdus++; connection->vccinf->vc_obytes += pdulen; tlen = 0; if (pad <= 8) mlen += pad; /* just "add" padding to * this buffer */ } *txqueue->scq_next++ = val1 | mlen; *txqueue->scq_next++ = vtophys(mptr); *txqueue->scq_next++ = val3; *txqueue->scq_next++ = val4; if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4) txqueue->scq_next = txqueue->scq_base; scq_space--; top->m_pkthdr.csum_data++; /* 12/22/2000 */ /* * if we need more than 8 bytes of padding, * use the zero-filled buffer defined above. */ if (tlen == 0 && pad > 8) { /* end of PDU, do we * need padding? */ val1 |= 0x40000000; /* last buffer in PDU */ *txqueue->scq_next++ = val1 | pad; *txqueue->scq_next++ = vtophys(padding); *txqueue->scq_next++ = val3; *txqueue->scq_next++ = val4; if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4) txqueue->scq_next = txqueue->scq_base; scq_space--; top->m_pkthdr.csum_data++; /* 12/22/2000 */ } } } /* * Now that we have set up the descriptors, add the entry * for Transmit Status Request so we know when the PDU(s) * are done. */ *txqueue->scq_next++ = 0xa0000000; /* TSR with interrupt */ *txqueue->scq_next++ = (u_long)top; *txqueue->scq_next++ = 0; *txqueue->scq_next++ = 0; if (txqueue->scq_next - txqueue->scq_base >= txqueue->scq_len * 4) txqueue->scq_next = txqueue->scq_base; scq_space--; top->m_pkthdr.csum_data++; /* 12/22/2000 */ count++; txqueue->scq_cur += top->m_pkthdr.csum_data; } /* * 05/31/2001: Optimization: Since writing to SRAM is very * expensive, we will only do this when the pointer is stale (half * of the queue). If the queue is less than 1/4 full, then write the * pointer anyway. */ if (idt_sysctl_qptrhold) { scq_space = txqueue->scq_next - txqueue->scq_last; /* number pending */ scq_space /= 4; if (scq_space < 0) scq_space += txqueue->scq_len; if (scq_space * 2 < txqueue->scq_len && /* less than half * pending */ txqueue->scq_cur > txqueue->scq_len / 4) /* and queue is active */ count = 0; } if (count) { /* we need to update the queue pointer */ nicstar_sram_wr(idt, txqueue->scd, 1, vtophys(txqueue->scq_next), 0, 0, 0); txqueue->scq_last = txqueue->scq_next; } splx(s); return; } /* Once a packet has been put in the Segmentation Channel Queue, it will * be sent, and then the mbuf will harvested by idt_intr_tsq(). While it * is in the SCQ, m_pkthdr.header is the pointer to the TX queue. This is * important because if the connection is closed while there are still * mbufs in the SCQ, idt_intr_tsq() still needs to update the TX queue. * ****************************************************************************** * * Handle entries in Transmit Status Queue (end of PDU interrupt or TSQ full) * * in: IDT device * * Date first: 12/04/2000 last: 06/10/2001 */ static void idt_intr_tsq(IDT * idt) { CONNECTION *connection; TX_QUEUE *txqueue; u_long *tsq_ptr; u_long val; struct mbuf *m; int count, s; s = splimp(); tsq_ptr = idt->tsq_head; count = 0; while ((tsq_ptr[1] & 0x80000000) == 0) { m = (struct mbuf *) tsq_ptr[0]; if (m != NULL) {/* first test for timer rollover entry */ if (((int)m & 0x000000ff)) /* now do sanity check * on the mbuf ptr */ device_printf(idt->dev, "DANGER! bad mbuf (%x), stamp=%x\n", (int)m, (int)tsq_ptr[1]); else { connection = (CONNECTION *) m->m_pkthdr.rcvif; txqueue = (TX_QUEUE *) m->m_pkthdr.header; txqueue->scq_cur -= m->m_pkthdr.csum_data; if (txqueue->scq_cur < 0 || txqueue->scq_cur > txqueue->scq_len) device_printf(idt->dev, "DANGER! scq_cur is %d\n", txqueue->scq_len); m->m_pkthdr.header = NULL; m_freem(m); idt_transmit_top(idt, txqueue); /* move more into queue */ } } tsq_ptr[0] = 0; tsq_ptr[1] = 0x80000000; /* reset TSQ entry */ tsq_ptr += 2; if (tsq_ptr >= idt->tsq_base + idt->tsq_size * 2) tsq_ptr = idt->tsq_base; count++; } idt->tsq_head = tsq_ptr; if (count) { val = (int)tsq_ptr - (int)idt->tsq_base; val -= 8; /* always stay one behind */ val &= 0x001ff8; *idt->reg_tsqh = val; } splx(s); } /* There is a problem with the pointer rollover where the SAR will think the * TSQ buffer is full (forever?) unless we hold the head pointer back. * This is not mentioned in the 77211 docs, but is a resolved issue in * revision D of the 77252 chips (see 77252 errata). * * If a connection is closed while there are still mbufs in the TX queue, * the connection TX queue pointer will be NULL. That is why we have a * special copy of the pointer in m_pkthdr.header. Also, idt_transmit_top() * will allow the TX queue for that connection to empty properly. * * It is possible for a TSQ entry to be 0x00ffffff/0x00ffffff, which is * obviously not an mbuf and not a timer rollover entry. We now have an * mbuf sanity check for this. * ****************************************************************************** * * nicstar_itrx ( card ) * * service error in transmitting PDU interrupt. * */ static void nicstar_itrx(nicstar_reg_t * idt) { /* trace mbuf and release */ } /****************************************************************************** * * Raw cell receive interrupt * * service raw cell reception interrupt. * */ static void nicstar_rawc(nicstar_reg_t * idt) { u_long ptr_tail; struct mbuf *qmbuf; u_long *qptr; u_long next_mbuf; u_long next_phys; if (idt->raw_headm == NULL || idt->raw_headp == 0) { device_printf(idt->dev, "RAW cell received, buffers not ready (%x/%x).\n", (int)idt->raw_headm, (int)idt->raw_headp); return; } ptr_tail = *(volatile u_long *)(idt->virt_baseaddr + REGRAWT); if ((ptr_tail & 0xfffff800) == idt->raw_headp) return; /* still in the same large buffer */ if ((ptr_tail & 0x7ff) < 64) /* wait until something in new buffer */ return; qmbuf = idt->raw_headm; qptr = (u_long *)qmbuf->m_data; next_mbuf = qptr[31 * 16 + 1]; /* next handle (virtual) */ next_phys = qptr[31 * 16 + 0]; /* next physical address */ /* if we want to do anything with the raw data, this is the place */ idt_mcheck_rem(idt, qmbuf); m_free(qmbuf); idt->raw_headm = (struct mbuf *) next_mbuf; idt->raw_headp = next_phys; } /***************************************************************************** * * Handle AAL5 PDU length * * in: IDT device, first mbuf in chain, last mbuf * out: zero = success, nz = failure (mbuf chain freed) * * Date first: 11/18/2000 last: 12/14/2000 */ int idt_receive_aal5(IDT * idt, struct mbuf * mfirst, struct mbuf * mdata) { struct mbuf *m2; unsigned char *aal5len; int plen; int diff; aal5len = mdata->m_data + mdata->m_len - 6; /* aal5 length = 16 bits */ plen = aal5len[0] * 256 + aal5len[1]; diff = mfirst->m_pkthdr.len - plen; /* number of bytes to trim */ if (diff == 0) return (0); if (diff < 0) { device_printf(idt->dev, "AAL5 PDU length (%d) greater than cells (%d), discarding\n", plen, mfirst->m_pkthdr.len); m_freem(mfirst); return (1); } while (mdata->m_len < diff) { /* last mbuf not big enough */ diff -= mdata->m_len; m2 = mdata; m_free(mdata); if (mdata == mfirst) { /* we just tossed the whole PDU */ device_printf(idt->dev, "AAL5 PDU length failed, discarding.\n"); return (1); /* the packetheadr length was bad! */ } for (mdata = mfirst; mdata->m_next != m2; mdata = mdata->m_next); mdata->m_next = NULL; /* remove old link to free'd mbuf */ } mdata->m_len -= diff; /* trim last mbuf */ mfirst->m_pkthdr.len = plen; return (0); } /* 12/14/2000: Removed "pruning" log message. * ***************************************************************************** * * nicstar_recv ( card ) * * rebuilds PDUs from entries in the Recieve Status Queue. * */ struct rsq_entry { u_long vpivci; struct mbuf *mdata; u_long crc; u_long flags; }; static void nicstar_recv(nicstar_reg_t * idt) { CONNECTION *connection; volatile u_long *regh = (volatile u_long *)(idt->virt_baseaddr + REGRSQH); struct rsq_entry *rsq; struct mbuf *mdata, *mptr; u_long flags; u_long crc; int vpi; int vci; int clen; int x, s; s = splimp(); rsq = (struct rsq_entry *) (idt->fixbuf + 0x2000 + (idt->rsqh & 0x1ffc)); if ((rsq->flags & 0x80000000) == 0) { splx(s); return; } while (rsq->flags & 0x80000000) { vpi = rsq->vpivci >> 16; /* first, grab the RSQ data */ vci = rsq->vpivci & 0xffff; mdata = rsq->mdata; crc = rsq->crc; flags = rsq->flags; clen = (flags & 0x1ff) * 48; rsq->vpivci = 0;/* now recycle the RSQ entry */ rsq->mdata = NULL; rsq->crc = 0; rsq->flags = 0; /* turn off valid bit */ rsq++; if (rsq == (struct rsq_entry *) (idt->fixbuf + 0x4000)) rsq = (struct rsq_entry *) (idt->fixbuf + 0x2000); idt_mcheck_rem(idt, mdata); connection = idt_connect_find(idt, vpi, vci); if (connection == NULL) { /* we don't want this PDU */ printf("nicstar_recv: No connection %d/%d - discarding packet.\n", vpi, vci); m_free(mdata); /* throw mbuf away */ continue; } mdata->m_len = clen; mptr = connection->recv; if (mptr == NULL) { if (mdata->m_flags & M_PKTHDR) connection->recv = mdata; else { idt->stats_ierrors++; /* 12/15/2000 */ connection->vccinf->vc_ierrors++; m_free(mdata); continue; } } else { x = 0; while (mptr->m_next != NULL) { /* find last mbuf in * chain */ mptr = mptr->m_next; x++; if (x > 25) break; } if (x > 25) { mptr = connection->recv; printf("nicstar_recv: invalid mbuf chain - probable corruption!\n"); m_free(mdata); idt->stats_ierrors++; /* 12/15/2000 */ connection->vccinf->vc_ierrors++; connection->recv = NULL; connection->rlen = 0; continue; } mptr->m_next = mdata; } connection->rlen += clen; if (flags & 0x2000) { /* end of PDU */ mptr = connection->recv; /* one or more mbufs * will be here */ clen = connection->rlen; /* length based on cell * count */ connection->recv = NULL; connection->rlen = 0; mptr->m_pkthdr.len = clen; mptr->m_pkthdr.rcvif = NULL; mptr->m_nextpkt = NULL; if (mptr->m_pkthdr.csum_flags) { device_printf(idt->dev, "received pkthdr.csum_flags=%x\n", mptr->m_pkthdr.csum_flags); mptr->m_pkthdr.csum_flags = 0; } if (flags & 0x200 && /* bad CRC */ idt->flg_igcrc == 0) { printf("nicstar_recv: Bad CRC - discarding PDU: %d/%d\n", vpi, vci); idt->stats_ierrors++; /* 12/15/2000 */ connection->vccinf->vc_ierrors++; m_freem(mptr); continue; } if (connection->aal == ATM_AAL5) { if (idt_receive_aal5(idt, mptr, mdata)) /* adjust for AAL5 * length */ continue; } idt->stats_ipdus++; /* 12/15/2000 */ idt->stats_ibytes += mptr->m_pkthdr.len; /* 12/15/2000 */ connection->vccinf->vc_ipdus++; connection->vccinf->vc_ibytes += mptr->m_pkthdr.len; idt_receive(idt, mptr, vpi, vci); } else if (connection->rlen > connection->maxpdu) { /* this packet is insane */ printf("nicstar_recv: Bad packet, len=%d - discarding.\n", connection->rlen); connection->recv = NULL; connection->rlen = 0; idt->stats_ierrors++; /* 12/15/2000 */ connection->vccinf->vc_ierrors++; m_freem(mptr); } /* end of PDU */ } idt->rsqh = vtophys((u_long)rsq) & 0x1ffc; *regh = (idt->rsqh - sizeof(struct rsq_entry)) & 0x1ff0; splx(s); } /****************************************************************************** * * Physical Interrupt handler * * service phyical interrupt. * */ static void nicstar_phys(nicstar_reg_t * idt) { u_long t; if (idt->flg_le25) { nicstar_util_rd(idt, 0x01, &t); /* get interrupt cause */ if (t & 0x01) { nicstar_util_wr(idt, 1, 0x02, 0x10); /* reset rx fifo */ device_printf(idt->dev, "PHY cleared.\n"); } } else device_printf(idt->dev, "Physical interrupt.\n"); } /****************************************************************************** * * Status register values */ #define STAT_REG_RSQAF 0x0002 /* receive status queue almost full */ #define STAT_REG_LBMT 0x0004 /* large buffer queue empty */ #define STAT_REG_SBMT 0x0008 /* small buffer queue empty */ #define STAT_REG_RAWC 0x0010 /* raw cell interrupt */ #define STAT_REG_EPDU 0x0020 /* end of PDU interrupt */ #define STAT_REG_PHY 0x0400 /* physical interrupt */ #define STAT_REG_TIME 0x0800 /* timer overflow interrupt */ #define STAT_REG_TSQAF 0x1000 /* transmit status queue almost full */ #define STAT_REG_TXIN 0x4000 /* TX PDU incomplete */ #define STAT_REG_TXOK 0x8000 /* TX status indicator */ /****************************************************************************** * * Interrupt handler * * service card interrupt. * * nicstar_intr ( card ) */ void nicstar_intr(void *arg) { IDT *idt; volatile u_long stat_val, config_val; int int_flags; volatile int i; int s; idt = (IDT *) arg; i = 0; s = splnet(); config_val = *idt->reg_cfg; stat_val = *idt->reg_stat; int_flags = STAT_REG_TSQAF | /* transmit status queue almost full */ STAT_REG_RSQAF | /* receive status queue almost full */ STAT_REG_RAWC | /* raw cell interrupt */ STAT_REG_EPDU | /* end of PDU interrupt */ STAT_REG_TIME | /* timer overflow interrupt */ STAT_REG_TXIN | /* TX PDU incomplete */ STAT_REG_TXOK; /* TX status indicator */ if (idt->flg_le25) int_flags |= STAT_REG_PHY; /* include flag for physical * interrupt */ if (stat_val & (STAT_REG_LBMT | STAT_REG_SBMT)) { /* buffer queue(s) empty */ if (stat_val & STAT_REG_SBMT) device_printf(idt->dev, "small free buffer queue empty.\n"); if (stat_val & STAT_REG_LBMT) device_printf(idt->dev, "large free buffer queue empty.\n"); nicstar_ld_rcv_buf(idt); if (*idt->reg_stat & STAT_REG_LBMT) { /* still empty, so * disable IRQ */ config_val &= ~0x01000000; *idt->reg_cfg = config_val; } } /* loop until no more interrupts to service */ while (stat_val & int_flags) { i++; if (i < 0 || i > 100) break; *idt->reg_stat = stat_val & int_flags; /* clear status bits */ if (stat_val & STAT_REG_EPDU) { /* receive PDU */ nicstar_recv(idt); nicstar_ld_rcv_buf(idt); /* replace buffers, * moved here 11/14/2000 */ } if (stat_val & STAT_REG_RAWC) { /* raw cell */ nicstar_rawc(idt); } if (stat_val & STAT_REG_TXOK) { /* transmit complete */ idt_intr_tsq(idt); } if (stat_val & STAT_REG_TXIN) { /* bad transmit */ nicstar_itrx(idt); device_printf(idt->dev, "Bad transmit.\n"); } if (stat_val & STAT_REG_TIME) { /* timer wrap */ idt->timer_wrap++; idt_intr_tsq(idt); /* check the TSQ */ nicstar_recv(idt); /* check the receive queue */ if (idt_sysctl_logbufs) idt_status_bufs(idt); /* show the buffer * status */ } if (stat_val & STAT_REG_PHY) { /* physical interrupt */ nicstar_phys(idt); *idt->reg_stat = STAT_REG_PHY; /* clear the int flag */ } if (stat_val & STAT_REG_RSQAF) { /* RSQ almost full */ nicstar_recv(idt); device_printf(idt->dev, "warning, RSQ almost full.\n"); if (*idt->reg_stat & STAT_REG_RSQAF) { /* RSQ full */ printf("RSQ is full, disabling interrupt.\n"); config_val &= 0x00000800; *idt->reg_cfg = config_val; } } if (stat_val & STAT_REG_TSQAF) { /* TSQ almost full */ idt_intr_tsq(idt); device_printf(idt->dev, "warning, TSQ almost full.\n"); if (*idt->reg_stat & STAT_REG_TSQAF) { printf("TSQ is full, disabling interrupt.\n"); config_val &= ~0x00000002; *idt->reg_cfg = config_val; } } stat_val = *idt->reg_stat; } splx(s); if (i < 1 || i > 50) device_printf(idt->dev, "i=%3d, status=%08x\n", i, (int)stat_val); }