/*- * Copyright (c) 2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * Author: Harti Brandt * * HARP pseudo-driver. This driver when loaded attaches to all ngATM drivers * in the system and creates a HARP physical interface for each of them. */ #include __FBSDID("$FreeBSD$"); #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 #define HARP_MTU 9188 /* * Physical interface softc */ struct harp_softc { Cmn_unit cmn; struct ifnet *parent; LIST_ENTRY(harp_softc) link; }; struct harp_vcc { struct cmn_vcc cmn; }; MODULE_VERSION(harp, 1); MODULE_DEPEND(harp, atm, 1, 1, 1); /* hooks from if_atmsubr.c */ extern void (*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m, struct atm_pseudohdr *ah, void *rxhand); extern void (*atm_harp_attach_p)(struct ifnet *); extern void (*atm_harp_detach_p)(struct ifnet *); static MALLOC_DEFINE(M_HARP, "harp", "Harp pseudo interface"); static uma_zone_t harp_nif_zone; static uma_zone_t harp_vcc_zone; /* List of all existing 'harp' interfaces */ static LIST_HEAD(, harp_softc) harp_softc_list = LIST_HEAD_INITIALIZER(harp_softc_list); static struct stack_defn harp_svaal5 = { NULL, SAP_CPCS_AAL5, SDF_TERM, atm_dev_inst, atm_dev_lower, NULL, 0, }; static struct stack_defn *harp_services = &harp_svaal5; /* * Map between constants */ static const struct { u_int vendor; u_int api; u_int dev; } map_devs[] = { [ATM_DEVICE_UNKNOWN] = { VENDOR_UNKNOWN, VENDAPI_UNKNOWN, DEV_UNKNOWN }, [ATM_DEVICE_PCA200E] = { VENDOR_FORE, VENDAPI_FORE_1, DEV_FORE_PCA200E }, [ATM_DEVICE_HE155] = { VENDOR_FORE, VENDAPI_FORE_2, DEV_FORE_HE155 }, [ATM_DEVICE_HE622] = { VENDOR_FORE, VENDAPI_FORE_2, DEV_FORE_HE622 }, [ATM_DEVICE_ENI155P] = { VENDOR_ENI, VENDAPI_ENI_1, DEV_ENI_155P }, [ATM_DEVICE_ADP155P] = { VENDOR_ENI, VENDAPI_ENI_1, DEV_ENI_155P }, [ATM_DEVICE_FORELE25] = { VENDOR_FORE, VENDAPI_IDT_1, DEV_FORE_LE25 }, [ATM_DEVICE_FORELE155] = { VENDOR_FORE, VENDAPI_IDT_1, DEV_FORE_LE155 }, [ATM_DEVICE_NICSTAR25] = { VENDOR_IDT, VENDAPI_IDT_1, DEV_IDT_25 }, [ATM_DEVICE_NICSTAR155] = { VENDOR_IDT, VENDAPI_IDT_1, DEV_IDT_155 }, [ATM_DEVICE_IDTABR25] = { VENDOR_IDT, VENDAPI_IDT_2, DEV_IDTABR_25 }, [ATM_DEVICE_IDTABR155] = { VENDOR_IDT, VENDAPI_IDT_2, DEV_IDTABR_155 }, [ATM_DEVICE_PROATM25] = { VENDOR_PROSUM, VENDAPI_IDT_2, DEV_PROATM_25 }, [ATM_DEVICE_PROATM155] = { VENDOR_PROSUM, VENDAPI_IDT_2, DEV_PROATM_155 }, }; /* * Return zero if this interface is ok for us. * XXX This should go away when we have full ngATM-ified the en driver. */ static int harp_check_if(const struct ifnet *ifp) { if (ifp->if_type == IFT_ATM && strcmp(ifp->if_dname, "en")) return (0); else return (-1); } /* * Instantiate a VCC stack. * * Could check for correct attributes here. */ static int harp_instvcc(Cmn_unit *up, Cmn_vcc *vp) { struct harp_softc *sc; if (up == NULL || vp == NULL || vp->cv_connvc == NULL) return (EINVAL); sc = (struct harp_softc *)up; return (0); } /* * Open a VCC. */ static int harp_openvcc(Cmn_unit *up, Cmn_vcc *vp) { struct harp_softc *sc; struct atmio_openvcc data; Atm_attributes *attrib; struct vccb *vccinf; const struct ifatm_mib *mib; int err; if (up == NULL || vp == NULL || vp->cv_connvc == NULL) return (EINVAL); sc = (struct harp_softc *)up; mib = sc->parent->if_linkmib; attrib = &vp->cv_connvc->cvc_attr; vccinf = vp->cv_connvc->cvc_vcc; if (attrib == NULL || vccinf == NULL) return (EINVAL); if (vccinf->vc_vpi >= (1 << mib->vpi_bits) || vccinf->vc_vci >= (1 << mib->vci_bits)) return (EINVAL); memset(&data, 0, sizeof(data)); switch (attrib->aal.type) { case ATM_AAL0: data.param.aal = ATMIO_AAL_0; break; case ATM_AAL5: data.param.aal = ATMIO_AAL_5; break; default: return (EINVAL); } data.param.vpi = vccinf->vc_vpi; data.param.vci = vccinf->vc_vci; data.param.rmtu = HARP_MTU; data.param.tmtu = HARP_MTU; switch (attrib->bearer.v.bearer_class) { case T_ATM_CLASS_C: data.param.traffic = ATMIO_TRAFFIC_VBR; break; case T_ATM_CLASS_X: switch (attrib->bearer.v.traffic_type) { case T_ATM_CBR: data.param.traffic = ATMIO_TRAFFIC_CBR; break; case T_ATM_VBR: data.param.traffic = ATMIO_TRAFFIC_VBR; break; case T_ATM_ABR: /* not really supported by HARP */ return (EINVAL); default: case T_ATM_UBR: data.param.traffic = ATMIO_TRAFFIC_UBR; break; } break; default: return (EINVAL); } data.param.tparam.pcr = attrib->traffic.v.forward.PCR_all_traffic; data.param.tparam.scr = attrib->traffic.v.forward.SCR_all_traffic; data.param.tparam.mbs = attrib->traffic.v.forward.MBS_all_traffic; data.rxhand = sc; data.param.flags = ATMIO_FLAG_HARP; err = (*sc->parent->if_ioctl)(sc->parent, SIOCATMOPENVCC, (caddr_t)&data); return (err); } /* * Close VCC */ static int harp_closevcc(Cmn_unit *up, Cmn_vcc *vp) { struct harp_softc *sc; struct atmio_closevcc data; int err; if (vp == NULL || vp->cv_connvc == NULL || vp->cv_connvc->cvc_vcc == NULL) return (EINVAL); sc = (struct harp_softc *)up; data.vpi = vp->cv_connvc->cvc_vcc->vc_vpi; data.vci = vp->cv_connvc->cvc_vcc->vc_vci; err = (*sc->parent->if_ioctl)(sc->parent, SIOCATMCLOSEVCC, (caddr_t)&data); return (err); } /* * IOCTLs */ static int harp_ioctl(int code, caddr_t addr, caddr_t arg) { return (ENOSYS); } /* * Output data */ static void harp_output(Cmn_unit *cu, Cmn_vcc *cv, KBuffer *m) { struct harp_softc *sc = (struct harp_softc *)cu; struct atm_pseudohdr *aph; int error; int mlen; if (cv == NULL || cv->cv_connvc == NULL || cv->cv_connvc->cvc_vcc == NULL) { m_freem(m); return; } M_ASSERTPKTHDR(m); /* * Harp seems very broken with regard to mbuf handling. The length * in the packet header is mostly broken here so recompute it. */ m->m_pkthdr.len = mlen = m_length(m, NULL); /* * Prepend pseudo-hdr. Drivers don't care about the flags. */ M_PREPEND(m, sizeof(*aph), M_DONTWAIT); if (m == NULL) return; aph = mtod(m, struct atm_pseudohdr *); ATM_PH_VPI(aph) = cv->cv_connvc->cvc_vcc->vc_vpi; ATM_PH_SETVCI(aph, cv->cv_connvc->cvc_vcc->vc_vci); ATM_PH_FLAGS(aph) = 0; error = atm_output(sc->parent, m, NULL, NULL); if (error) { printf("%s: error %d\n", __func__, error); sc->cmn.cu_pif.pif_oerrors++; cv->cv_connvc->cvc_vcc->vc_oerrors++; if (cv->cv_connvc->cvc_vcc->vc_nif) cv->cv_connvc->cvc_vcc->vc_nif->nif_if.if_oerrors++; return; } /* statistics */ sc->cmn.cu_pif.pif_opdus++; sc->cmn.cu_pif.pif_obytes += mlen; cv->cv_connvc->cvc_vcc->vc_opdus++; cv->cv_connvc->cvc_vcc->vc_obytes += mlen; if (cv->cv_connvc->cvc_vcc->vc_nif) { cv->cv_connvc->cvc_vcc->vc_nif->nif_obytes += mlen; cv->cv_connvc->cvc_vcc->vc_nif->nif_if.if_obytes += mlen; cv->cv_connvc->cvc_vcc->vc_nif->nif_if.if_opackets++; } } /* * Attach a new interface */ static void harp_attach(struct ifnet *parent) { struct harp_softc *sc; const struct ifatm_mib *mib; int error; if (harp_check_if(parent) != 0) return; sc = malloc(sizeof(*sc), M_HARP, M_WAITOK | M_ZERO); sc->parent = parent; sc->cmn.cu_unit = parent->if_dunit; sc->cmn.cu_mtu = HARP_MTU; sc->cmn.cu_ioctl = harp_ioctl; sc->cmn.cu_instvcc = harp_instvcc; sc->cmn.cu_openvcc = harp_openvcc; sc->cmn.cu_closevcc = harp_closevcc; sc->cmn.cu_output = harp_output; sc->cmn.cu_vcc_zone = harp_vcc_zone; sc->cmn.cu_nif_zone = harp_nif_zone; sc->cmn.cu_softc = sc; /* config */ mib = parent->if_linkmib; if (mib->device >= sizeof(map_devs) / sizeof(map_devs[0])) { sc->cmn.cu_config.ac_vendor = VENDOR_UNKNOWN; sc->cmn.cu_config.ac_vendapi = VENDAPI_UNKNOWN; sc->cmn.cu_config.ac_device = DEV_UNKNOWN; } else { sc->cmn.cu_config.ac_vendor = map_devs[mib->device].vendor; sc->cmn.cu_config.ac_vendapi = map_devs[mib->device].api; sc->cmn.cu_config.ac_device = map_devs[mib->device].dev; } switch (mib->media) { case IFM_ATM_UTP_25: sc->cmn.cu_config.ac_media = MEDIA_UTP25;; break; case IFM_ATM_TAXI_100: sc->cmn.cu_config.ac_media = MEDIA_TAXI_100; break; case IFM_ATM_TAXI_140: sc->cmn.cu_config.ac_media = MEDIA_TAXI_140; break; case IFM_ATM_MM_155: case IFM_ATM_SM_155: sc->cmn.cu_config.ac_media = MEDIA_OC3C; break; case IFM_ATM_MM_622: case IFM_ATM_SM_622: sc->cmn.cu_config.ac_media = MEDIA_OC12C; break; case IFM_ATM_UTP_155: sc->cmn.cu_config.ac_media = MEDIA_UTP155; break; default: sc->cmn.cu_config.ac_media = MEDIA_UNKNOWN; break; } sc->cmn.cu_config.ac_bustype = BUS_PCI; sc->cmn.cu_pif.pif_pcr = mib->pcr; sc->cmn.cu_pif.pif_maxvpi = (1 << mib->vpi_bits) - 1; sc->cmn.cu_pif.pif_maxvci = (1 << mib->vci_bits) - 1; snprintf(sc->cmn.cu_config.ac_hard_vers, sizeof(sc->cmn.cu_config.ac_hard_vers), "0x%lx", (u_long)mib->hw_version); snprintf(sc->cmn.cu_config.ac_firm_vers, sizeof(sc->cmn.cu_config.ac_firm_vers), "0x%lx", (u_long)mib->sw_version); sc->cmn.cu_config.ac_serial = mib->serial; sc->cmn.cu_config.ac_ram = 0; sc->cmn.cu_config.ac_ramsize = 0; sc->cmn.cu_config.ac_macaddr.ma_data[0] = sc->cmn.cu_pif.pif_macaddr.ma_data[0] = mib->esi[0]; sc->cmn.cu_config.ac_macaddr.ma_data[1] = sc->cmn.cu_pif.pif_macaddr.ma_data[1] = mib->esi[1]; sc->cmn.cu_config.ac_macaddr.ma_data[2] = sc->cmn.cu_pif.pif_macaddr.ma_data[2] = mib->esi[2]; sc->cmn.cu_config.ac_macaddr.ma_data[3] = sc->cmn.cu_pif.pif_macaddr.ma_data[3] = mib->esi[3]; sc->cmn.cu_config.ac_macaddr.ma_data[4] = sc->cmn.cu_pif.pif_macaddr.ma_data[4] = mib->esi[4]; sc->cmn.cu_config.ac_macaddr.ma_data[5] = sc->cmn.cu_pif.pif_macaddr.ma_data[5] = mib->esi[5]; error = atm_physif_register(&sc->cmn, parent->if_dname, harp_services); if (error) { log(LOG_ERR, "%s: pif registration failed %d\n", parent->if_dname, error); free(sc, M_HARP); return; } LIST_INSERT_HEAD(&harp_softc_list, sc, link); sc->cmn.cu_flags |= CUF_INITED; } /* * Destroy a cloned device */ static void harp_detach(struct ifnet *ifp) { struct harp_softc *sc; int error; LIST_FOREACH(sc, &harp_softc_list, link) if (sc->parent == ifp) break; if (sc == NULL) return; error = atm_physif_deregister(&sc->cmn); if (error) log(LOG_ERR, "%s: de-registration failed %d\n", ifp->if_dname, error); LIST_REMOVE(sc, link); free(sc, M_HARP); } /* * Pass PDU up the stack */ static void harp_recv_stack(void *tok, KBuffer *m) { Cmn_vcc *vcc = tok; int err; M_ASSERTPKTHDR(m); STACK_CALL(CPCS_UNITDATA_SIG, vcc->cv_upper, vcc->cv_toku, vcc->cv_connvc, (intptr_t)m, 0, err); if (err) { printf("%s: error %d\n", __func__, err); KB_FREEALL(m); } } /* * Possible input from NATM */ static void harp_input(struct ifnet *ifp, struct mbuf **mp, struct atm_pseudohdr *ah, void *rxhand) { struct harp_softc *sc = rxhand; Cmn_vcc *vcc; char *cp; u_int pfxlen; struct mbuf *m, *m0; int mlen; if ((ATM_PH_FLAGS(ah) & ATMIO_FLAG_HARP) == 0) return; /* grab the packet */ m = *mp; *mp = NULL; if (sc->parent != ifp) { printf("%s: parent=%p ifp=%p\n", __func__, sc->parent, ifp); goto drop; } vcc = atm_dev_vcc_find(&sc->cmn, ATM_PH_VPI(ah), ATM_PH_VCI(ah), VCC_IN); if (vcc == NULL) { printf("%s: VCC %u/%u not found\n", __func__,ATM_PH_VPI(ah), ATM_PH_VCI(ah)); goto drop; } /* fit two pointers into the mbuf - assume, that the the data is * pointer aligned. If it doesn't fit into the first mbuf, prepend * another one. * Don't count the new fields in the packet length (XXX) */ mlen = m->m_pkthdr.len; pfxlen = sizeof(atm_intr_func_t) + sizeof(void *); if (M_LEADINGSPACE(m) < pfxlen) { MGETHDR(m0, 0, MT_DATA); if (m0 == NULL) { printf("%s: no leading space in buffer\n", __func__); goto drop; } m0->m_len = 0; m0->m_next = m; M_MOVE_PKTHDR(m0, m); m = m0; } m->m_len += pfxlen; m->m_data -= pfxlen; cp = mtod(m, char *); *((atm_intr_func_t *)cp) = harp_recv_stack; cp += sizeof(atm_intr_func_t); *((void **)cp) = (void *)vcc; /* count the packet */ sc->cmn.cu_pif.pif_ipdus++; sc->cmn.cu_pif.pif_ibytes += mlen; vcc->cv_connvc->cvc_vcc->vc_ipdus++; vcc->cv_connvc->cvc_vcc->vc_ibytes += mlen; if (vcc->cv_connvc->cvc_vcc->vc_nif) { vcc->cv_connvc->cvc_vcc->vc_nif->nif_ibytes += mlen; vcc->cv_connvc->cvc_vcc->vc_nif->nif_if.if_ipackets++; vcc->cv_connvc->cvc_vcc->vc_nif->nif_if.if_ibytes += mlen; } /* hand it off */ netisr_dispatch(NETISR_ATM, m); return; drop: m_freem(m); } /* * Module loading/unloading */ static int harp_modevent(module_t mod, int event, void *data) { struct ifnet *ifp; switch (event) { case MOD_LOAD: harp_nif_zone = uma_zcreate("harp nif", sizeof(struct atm_nif), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); if (harp_nif_zone == NULL) panic("%s: nif_zone", __func__); harp_vcc_zone = uma_zcreate("harp vcc", sizeof(struct harp_vcc), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); if (harp_vcc_zone == NULL) panic("%s: vcc_zone", __func__); /* Create harp interfaces for all existing ATM interfaces */ TAILQ_FOREACH(ifp, &ifnet, if_link) harp_attach(ifp); atm_harp_attach_p = harp_attach; atm_harp_detach_p = harp_detach; atm_harp_input_p = harp_input; break; case MOD_UNLOAD: atm_harp_attach_p = NULL; atm_harp_detach_p = NULL; atm_harp_input_p = NULL; while (!LIST_EMPTY(&harp_softc_list)) harp_detach(LIST_FIRST(&harp_softc_list)->parent); uma_zdestroy(harp_nif_zone); uma_zdestroy(harp_vcc_zone); break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t harp_mod = { "if_harp", harp_modevent, 0 }; DECLARE_MODULE(harp, harp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);