summaryrefslogtreecommitdiffstats
path: root/rtems/freebsd/net/if_atmsubr.c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@oarcorp.com>2012-03-07 09:52:04 -0600
committerJoel Sherrill <joel.sherrill@oarcorp.com>2012-03-07 09:52:04 -0600
commita9153ec3040f54fa52b68e14dafed2aba7b780ae (patch)
treefda80e3380dfebf7d97868507aa185757852e882 /rtems/freebsd/net/if_atmsubr.c
downloadrtems-libbsd-a9153ec3040f54fa52b68e14dafed2aba7b780ae.tar.bz2
Initial import
Code is based on FreeBSD 8.2 with USB support from Sebastian Huber and Thomas Doerfler. Initial TCP/IP stack work is from Kevel Kirspel.
Diffstat (limited to 'rtems/freebsd/net/if_atmsubr.c')
-rw-r--r--rtems/freebsd/net/if_atmsubr.c504
1 files changed, 504 insertions, 0 deletions
diff --git a/rtems/freebsd/net/if_atmsubr.c b/rtems/freebsd/net/if_atmsubr.c
new file mode 100644
index 00000000..b053aaaf
--- /dev/null
+++ b/rtems/freebsd/net/if_atmsubr.c
@@ -0,0 +1,504 @@
+#include <rtems/freebsd/machine/rtems-bsd-config.h>
+
+/* $NetBSD: if_atmsubr.c,v 1.10 1997/03/11 23:19:51 chuck Exp $ */
+
+/*-
+ *
+ * Copyright (c) 1996 Charles D. Cranor and Washington University.
+ * 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 Charles D. Cranor and
+ * Washington University.
+ * 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.
+ *
+ * if_atmsubr.c
+ */
+
+#include <rtems/freebsd/sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/freebsd/local/opt_inet.h>
+#include <rtems/freebsd/local/opt_inet6.h>
+#include <rtems/freebsd/local/opt_natm.h>
+
+#include <rtems/freebsd/sys/param.h>
+#include <rtems/freebsd/sys/systm.h>
+#include <rtems/freebsd/sys/kernel.h>
+#include <rtems/freebsd/sys/module.h>
+#include <rtems/freebsd/sys/mbuf.h>
+#include <rtems/freebsd/sys/socket.h>
+#include <rtems/freebsd/sys/sockio.h>
+#include <rtems/freebsd/sys/errno.h>
+#include <rtems/freebsd/sys/sysctl.h>
+#include <rtems/freebsd/sys/malloc.h>
+
+#include <rtems/freebsd/net/if.h>
+#include <rtems/freebsd/net/netisr.h>
+#include <rtems/freebsd/net/route.h>
+#include <rtems/freebsd/net/if_dl.h>
+#include <rtems/freebsd/net/if_types.h>
+#include <rtems/freebsd/net/if_atm.h>
+
+#include <rtems/freebsd/netinet/in.h>
+#include <rtems/freebsd/netinet/if_atm.h>
+#include <rtems/freebsd/netinet/if_ether.h> /* XXX: for ETHERTYPE_* */
+#if defined(INET) || defined(INET6)
+#include <rtems/freebsd/netinet/in_var.h>
+#endif
+#ifdef NATM
+#include <rtems/freebsd/netnatm/natm.h>
+#endif
+
+#include <rtems/freebsd/security/mac/mac_framework.h>
+
+/*
+ * Netgraph interface functions.
+ * These need not be protected by a lock, because ng_atm nodes are persitent.
+ * The ng_atm module can be unloaded only if all ATM interfaces have been
+ * unloaded, so nobody should be in the code paths accessing these function
+ * pointers.
+ */
+void (*ng_atm_attach_p)(struct ifnet *);
+void (*ng_atm_detach_p)(struct ifnet *);
+int (*ng_atm_output_p)(struct ifnet *, struct mbuf **);
+void (*ng_atm_input_p)(struct ifnet *, struct mbuf **,
+ struct atm_pseudohdr *, void *);
+void (*ng_atm_input_orphan_p)(struct ifnet *, struct mbuf *,
+ struct atm_pseudohdr *, void *);
+void (*ng_atm_event_p)(struct ifnet *, uint32_t, void *);
+
+/*
+ * Harp pseudo interface hooks
+ */
+void (*atm_harp_input_p)(struct ifnet *ifp, struct mbuf **m,
+ struct atm_pseudohdr *ah, void *rxhand);
+void (*atm_harp_attach_p)(struct ifnet *);
+void (*atm_harp_detach_p)(struct ifnet *);
+void (*atm_harp_event_p)(struct ifnet *, uint32_t, void *);
+
+SYSCTL_NODE(_hw, OID_AUTO, atm, CTLFLAG_RW, 0, "ATM hardware");
+
+MALLOC_DEFINE(M_IFATM, "ifatm", "atm interface internals");
+
+#ifndef ETHERTYPE_IPV6
+#define ETHERTYPE_IPV6 0x86dd
+#endif
+
+#define senderr(e) do { error = (e); goto bad; } while (0)
+
+/*
+ * atm_output: ATM output routine
+ * inputs:
+ * "ifp" = ATM interface to output to
+ * "m0" = the packet to output
+ * "dst" = the sockaddr to send to (either IP addr, or raw VPI/VCI)
+ * "ro" = the route to use
+ * returns: error code [0 == ok]
+ *
+ * note: special semantic: if (dst == NULL) then we assume "m" already
+ * has an atm_pseudohdr on it and just send it directly.
+ * [for native mode ATM output] if dst is null, then
+ * ro->ro_rt must also be NULL.
+ */
+int
+atm_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst,
+ struct route *ro)
+{
+ u_int16_t etype = 0; /* if using LLC/SNAP */
+ int error = 0, sz;
+ struct atm_pseudohdr atmdst, *ad;
+ struct mbuf *m = m0;
+ struct atmllc *atmllc;
+ struct atmllc *llc_hdr = NULL;
+ u_int32_t atm_flags;
+
+#ifdef MAC
+ error = mac_ifnet_check_transmit(ifp, m);
+ if (error)
+ senderr(error);
+#endif
+
+ if (!((ifp->if_flags & IFF_UP) &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING)))
+ senderr(ENETDOWN);
+
+ /*
+ * check for non-native ATM traffic (dst != NULL)
+ */
+ if (dst) {
+ switch (dst->sa_family) {
+
+#if defined(INET) || defined(INET6)
+ case AF_INET:
+ case AF_INET6:
+ {
+ if (dst->sa_family == AF_INET6)
+ etype = ETHERTYPE_IPV6;
+ else
+ etype = ETHERTYPE_IP;
+ if (!atmresolve(ro->ro_rt, m, dst, &atmdst)) {
+ m = NULL;
+ /* XXX: atmresolve already free'd it */
+ senderr(EHOSTUNREACH);
+ /* XXX: put ATMARP stuff here */
+ /* XXX: watch who frees m on failure */
+ }
+ }
+ break;
+#endif /* INET || INET6 */
+
+ case AF_UNSPEC:
+ /*
+ * XXX: bpfwrite. assuming dst contains 12 bytes
+ * (atm pseudo header (4) + LLC/SNAP (8))
+ */
+ bcopy(dst->sa_data, &atmdst, sizeof(atmdst));
+ llc_hdr = (struct atmllc *)(dst->sa_data +
+ sizeof(atmdst));
+ break;
+
+ default:
+ printf("%s: can't handle af%d\n", ifp->if_xname,
+ dst->sa_family);
+ senderr(EAFNOSUPPORT);
+ }
+
+ /*
+ * must add atm_pseudohdr to data
+ */
+ sz = sizeof(atmdst);
+ atm_flags = ATM_PH_FLAGS(&atmdst);
+ if (atm_flags & ATM_PH_LLCSNAP)
+ sz += 8; /* sizeof snap == 8 */
+ M_PREPEND(m, sz, M_DONTWAIT);
+ if (m == 0)
+ senderr(ENOBUFS);
+ ad = mtod(m, struct atm_pseudohdr *);
+ *ad = atmdst;
+ if (atm_flags & ATM_PH_LLCSNAP) {
+ atmllc = (struct atmllc *)(ad + 1);
+ if (llc_hdr == NULL) {
+ bcopy(ATMLLC_HDR, atmllc->llchdr,
+ sizeof(atmllc->llchdr));
+ /* note: in host order */
+ ATM_LLC_SETTYPE(atmllc, etype);
+ }
+ else
+ bcopy(llc_hdr, atmllc, sizeof(struct atmllc));
+ }
+ }
+
+ if (ng_atm_output_p != NULL) {
+ if ((error = (*ng_atm_output_p)(ifp, &m)) != 0) {
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+ }
+ if (m == NULL)
+ return (0);
+ }
+
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ if (!IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp,
+ -(int)sizeof(struct atm_pseudohdr)))
+ return (ENOBUFS);
+ return (error);
+
+bad:
+ if (m)
+ m_freem(m);
+ return (error);
+}
+
+/*
+ * Process a received ATM packet;
+ * the packet is in the mbuf chain m.
+ */
+void
+atm_input(struct ifnet *ifp, struct atm_pseudohdr *ah, struct mbuf *m,
+ void *rxhand)
+{
+ int isr;
+ u_int16_t etype = ETHERTYPE_IP; /* default */
+
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ m_freem(m);
+ return;
+ }
+#ifdef MAC
+ mac_ifnet_create_mbuf(ifp, m);
+#endif
+ ifp->if_ibytes += m->m_pkthdr.len;
+
+ if (ng_atm_input_p != NULL) {
+ (*ng_atm_input_p)(ifp, &m, ah, rxhand);
+ if (m == NULL)
+ return;
+ }
+
+ /* not eaten by ng_atm. Maybe it's a pseudo-harp PDU? */
+ if (atm_harp_input_p != NULL) {
+ (*atm_harp_input_p)(ifp, &m, ah, rxhand);
+ if (m == NULL)
+ return;
+ }
+
+ if (rxhand) {
+#ifdef NATM
+ struct natmpcb *npcb;
+
+ /*
+ * XXXRW: this use of 'rxhand' is not a very good idea, and
+ * was subject to races even before SMPng due to the release
+ * of spl here.
+ */
+ NATM_LOCK();
+ npcb = rxhand;
+ npcb->npcb_inq++; /* count # in queue */
+ isr = NETISR_NATM;
+ m->m_pkthdr.rcvif = rxhand; /* XXX: overload */
+ NATM_UNLOCK();
+#else
+ printf("atm_input: NATM detected but not "
+ "configured in kernel\n");
+ goto dropit;
+#endif
+ } else {
+ /*
+ * handle LLC/SNAP header, if present
+ */
+ if (ATM_PH_FLAGS(ah) & ATM_PH_LLCSNAP) {
+ struct atmllc *alc;
+
+ if (m->m_len < sizeof(*alc) &&
+ (m = m_pullup(m, sizeof(*alc))) == 0)
+ return; /* failed */
+ alc = mtod(m, struct atmllc *);
+ if (bcmp(alc, ATMLLC_HDR, 6)) {
+ printf("%s: recv'd invalid LLC/SNAP frame "
+ "[vp=%d,vc=%d]\n", ifp->if_xname,
+ ATM_PH_VPI(ah), ATM_PH_VCI(ah));
+ m_freem(m);
+ return;
+ }
+ etype = ATM_LLC_TYPE(alc);
+ m_adj(m, sizeof(*alc));
+ }
+
+ switch (etype) {
+
+#ifdef INET
+ case ETHERTYPE_IP:
+ isr = NETISR_IP;
+ break;
+#endif
+
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ isr = NETISR_IPV6;
+ break;
+#endif
+ default:
+#ifndef NATM
+ dropit:
+#endif
+ if (ng_atm_input_orphan_p != NULL)
+ (*ng_atm_input_orphan_p)(ifp, m, ah, rxhand);
+ else
+ m_freem(m);
+ return;
+ }
+ }
+ netisr_dispatch(isr, m);
+}
+
+/*
+ * Perform common duties while attaching to interface list.
+ */
+void
+atm_ifattach(struct ifnet *ifp)
+{
+ struct ifaddr *ifa;
+ struct sockaddr_dl *sdl;
+ struct ifatm *ifatm = ifp->if_l2com;
+
+ ifp->if_addrlen = 0;
+ ifp->if_hdrlen = 0;
+ if_attach(ifp);
+ ifp->if_mtu = ATMMTU;
+ ifp->if_output = atm_output;
+#if 0
+ ifp->if_input = atm_input;
+#endif
+ ifp->if_snd.ifq_maxlen = 50; /* dummy */
+
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
+ if (ifa->ifa_addr->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ sdl->sdl_type = IFT_ATM;
+ sdl->sdl_alen = ifp->if_addrlen;
+#ifdef notyet /* if using ATMARP, store hardware address using the next line */
+ bcopy(ifp->hw_addr, LLADDR(sdl), ifp->if_addrlen);
+#endif
+ break;
+ }
+
+ ifp->if_linkmib = &ifatm->mib;
+ ifp->if_linkmiblen = sizeof(ifatm->mib);
+
+ if(ng_atm_attach_p)
+ (*ng_atm_attach_p)(ifp);
+ if (atm_harp_attach_p)
+ (*atm_harp_attach_p)(ifp);
+}
+
+/*
+ * Common stuff for detaching an ATM interface
+ */
+void
+atm_ifdetach(struct ifnet *ifp)
+{
+ if (atm_harp_detach_p)
+ (*atm_harp_detach_p)(ifp);
+ if(ng_atm_detach_p)
+ (*ng_atm_detach_p)(ifp);
+ if_detach(ifp);
+}
+
+/*
+ * Support routine for the SIOCATMGVCCS ioctl().
+ *
+ * This routine assumes, that the private VCC structures used by the driver
+ * begin with a struct atmio_vcc.
+ *
+ * Return a table of VCCs in a freshly allocated memory area.
+ * Here we have a problem: we first count, how many vccs we need
+ * to return. The we allocate the memory and finally fill it in.
+ * Because we cannot lock while calling malloc, the number of active
+ * vccs may change while we're in malloc. So we allocate a couple of
+ * vccs more and if space anyway is not enough re-iterate.
+ *
+ * We could use an sx lock for the vcc tables.
+ */
+struct atmio_vcctable *
+atm_getvccs(struct atmio_vcc **table, u_int size, u_int start,
+ struct mtx *lock, int waitok)
+{
+ u_int cid, alloc;
+ size_t len;
+ struct atmio_vcctable *vccs;
+ struct atmio_vcc *v;
+
+ alloc = start + 10;
+ vccs = NULL;
+
+ for (;;) {
+ len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]);
+ vccs = reallocf(vccs, len, M_TEMP,
+ waitok ? M_WAITOK : M_NOWAIT);
+ if (vccs == NULL)
+ return (NULL);
+ bzero(vccs, len);
+
+ vccs->count = 0;
+ v = vccs->vccs;
+
+ mtx_lock(lock);
+ for (cid = 0; cid < size; cid++)
+ if (table[cid] != NULL) {
+ if (++vccs->count == alloc)
+ /* too many - try again */
+ break;
+ *v++ = *table[cid];
+ }
+ mtx_unlock(lock);
+
+ if (cid == size)
+ break;
+
+ alloc *= 2;
+ }
+ return (vccs);
+}
+
+/*
+ * Driver or channel state has changed. Inform whoever is interested
+ * in these events.
+ */
+void
+atm_event(struct ifnet *ifp, u_int event, void *arg)
+{
+ if (ng_atm_event_p != NULL)
+ (*ng_atm_event_p)(ifp, event, arg);
+ if (atm_harp_event_p != NULL)
+ (*atm_harp_event_p)(ifp, event, arg);
+}
+
+static void *
+atm_alloc(u_char type, struct ifnet *ifp)
+{
+ struct ifatm *ifatm;
+
+ ifatm = malloc(sizeof(struct ifatm), M_IFATM, M_WAITOK | M_ZERO);
+ ifatm->ifp = ifp;
+
+ return (ifatm);
+}
+
+static void
+atm_free(void *com, u_char type)
+{
+
+ free(com, M_IFATM);
+}
+
+static int
+atm_modevent(module_t mod, int type, void *data)
+{
+ switch (type) {
+ case MOD_LOAD:
+ if_register_com_alloc(IFT_ATM, atm_alloc, atm_free);
+ break;
+ case MOD_UNLOAD:
+ if_deregister_com_alloc(IFT_ATM);
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ return (0);
+}
+
+static moduledata_t atm_mod = {
+ "atm",
+ atm_modevent,
+ 0
+};
+
+DECLARE_MODULE(atm, atm_mod, SI_SUB_INIT_IF, SI_ORDER_ANY);
+MODULE_VERSION(atm, 1);