/*- * 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. * ****************************************************************************** */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /****************************************************************************** * * 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 #define iu_softc iu_cmn.cu_softc /* * 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 *); #ifdef T_ATM_BUFQUEUE static CONNECTION *idt_atm_harpconn(Cmn_unit *, Cmn_vcc *); #endif 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); } #ifdef T_ATM_BUFQUEUE /******************************************************************************* * * 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)); } #endif /* T_ATM_BUFQUEUE */ /******************************************************************************* * * 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; idt->iu_softc = (void *)idt; /* * 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 = 0; 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 */ netisr_queue(NETISR_ATM, m); /* mbuf is free'd on failure. */ }