freebsd-dev/sys/dev/harp/if_harp.c

662 lines
16 KiB
C

/*-
* 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 <harti@freebsd.org>
*
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/syslog.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <net/netisr.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <netatm/atm_vc.h>
#include <net/if_atm.h>
#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);