summaryrefslogtreecommitdiffstats
path: root/sys/dev/patm/if_patm_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/patm/if_patm_ioctl.c')
-rw-r--r--sys/dev/patm/if_patm_ioctl.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/sys/dev/patm/if_patm_ioctl.c b/sys/dev/patm/if_patm_ioctl.c
new file mode 100644
index 0000000..c91e714
--- /dev/null
+++ b/sys/dev/patm/if_patm_ioctl.c
@@ -0,0 +1,450 @@
+/*
+ * 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: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Driver for IDT77252 based cards like ProSum's.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+#include "opt_natm.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/errno.h>
+#include <sys/conf.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/queue.h>
+#include <sys/condvar.h>
+#include <vm/uma.h>
+
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_atm.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/if_atm.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/mbpool.h>
+
+#include <dev/utopia/utopia.h>
+#include <dev/patm/idt77252reg.h>
+#include <dev/patm/if_patmvar.h>
+
+/*
+ * Open the VCC with the given parameters
+ */
+static int
+patm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg, u_int async)
+{
+ u_int cid;
+ struct patm_vcc *vcc;
+ int error = 0;
+
+ patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi,
+ arg->param.vci, arg->param.flags);
+
+ if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci))
+ return (EINVAL);
+ if (arg->param.vci == 0 && (arg->param.vpi != 0 ||
+ !(arg->param.flags & ATMIO_FLAG_NOTX) ||
+ arg->param.aal != ATMIO_AAL_RAW))
+ return (EINVAL);
+ cid = PATM_CID(sc, arg->param.vpi, arg->param.vci);
+
+ if ((arg->param.flags & ATMIO_FLAG_NOTX) &&
+ (arg->param.flags & ATMIO_FLAG_NORX))
+ return (EINVAL);
+
+ if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) &&
+ (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX)))
+ return (EINVAL);
+
+ /* allocate vcc */
+ vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO);
+ if (vcc == NULL)
+ return (ENOMEM);
+
+ mtx_lock(&sc->mtx);
+ if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
+ /* stopped while we have analyzed the arguments */
+ error = EIO;
+ goto done;
+ }
+ if (sc->vccs[cid] != NULL) {
+ /* ups, already open */
+ error = EBUSY;
+ goto done;
+ }
+
+ /* check some parameters */
+ vcc->cid = cid;
+ vcc->vcc = arg->param;
+ vcc->vflags = async;
+ vcc->rxhand = arg->rxhand;
+ switch (vcc->vcc.aal) {
+
+ case ATMIO_AAL_0:
+ case ATMIO_AAL_34:
+ case ATMIO_AAL_5:
+ break;
+
+ case ATMIO_AAL_RAW:
+ if (arg->param.vci == 0 &&
+ !(arg->param.flags & ATMIO_FLAG_NOTX)) {
+ error = EINVAL;
+ goto done;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ goto done;
+ }
+ switch (vcc->vcc.traffic) {
+
+ case ATMIO_TRAFFIC_VBR:
+ case ATMIO_TRAFFIC_UBR:
+ case ATMIO_TRAFFIC_CBR:
+ case ATMIO_TRAFFIC_ABR:
+ break;
+
+ default:
+ error = EINVAL;
+ goto done;
+ }
+
+ /* initialize */
+ vcc->chain = NULL;
+ vcc->last = NULL;
+ vcc->ibytes = vcc->ipackets = 0;
+ vcc->obytes = vcc->opackets = 0;
+
+ /* ask the TX and RX sides */
+ patm_debug(sc, VCC, "Open VCC: asking Rx/Tx");
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) &&
+ (error = patm_tx_vcc_can_open(sc, vcc)) != 0)
+ goto done;
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) &&
+ (error = patm_rx_vcc_can_open(sc, vcc)) != 0)
+ goto done;
+
+ /* ok - go ahead */
+ sc->vccs[cid] = vcc;
+
+ patm_debug(sc, VCC, "Open VCC: opening");
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
+ patm_tx_vcc_open(sc, vcc);
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
+ patm_rx_vcc_open(sc, vcc);
+
+#ifdef notyet
+ /* inform management about non-NG and NG-PVCs */
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
+ (vcc->vcc.flags & ATMIO_FLAG_PVC))
+ atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
+ (1 << 24) | (vcc->vcc.vpi << 16) | vcc->vcc.vci);
+#endif
+
+ patm_debug(sc, VCC, "Open VCC: now open");
+
+ /* don't free below */
+ vcc = NULL;
+
+ sc->vccs_open++;
+
+ /* done */
+ done:
+ mtx_unlock(&sc->mtx);
+ if (vcc != NULL)
+ uma_zfree(sc->vcc_zone, vcc);
+ return (error);
+}
+
+/*
+ * Enable ioctl for NATM. Map to an open ioctl.
+ */
+static int
+patm_open_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph)
+{
+ struct atmio_openvcc v;
+
+ bzero(&v, sizeof(v));
+ v.param.flags = ATM_PH_FLAGS(&ph->aph) & (ATM_PH_AAL5 | ATM_PH_LLCSNAP);
+ v.param.vpi = ATM_PH_VPI(&ph->aph);
+ v.param.vci = ATM_PH_VCI(&ph->aph);
+ v.param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5)
+ ? ATMIO_AAL_5 : ATMIO_AAL_0;
+ v.param.traffic = ATMIO_TRAFFIC_UBR;;
+ v.param.tparam.pcr = sc->ifatm.mib.pcr;
+ v.rxhand = ph->rxhand;
+
+ return (patm_open_vcc(sc, &v, PATM_VCC_ASYNC));
+}
+
+/*
+ * Try to close the given VCC
+ */
+static int
+patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg)
+{
+ u_int cid;
+ struct patm_vcc *vcc;
+ int error = 0;
+
+ patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci);
+
+ if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci))
+ return (EINVAL);
+ cid = PATM_CID(sc, arg->vpi, arg->vci);
+
+ mtx_lock(&sc->mtx);
+ if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
+ /* stopped while we have analyzed the arguments */
+ error = EIO;
+ goto done;
+ }
+
+ vcc = sc->vccs[cid];
+ if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) {
+ error = ENOENT;
+ goto done;
+ }
+
+ if (vcc->vflags & PATM_VCC_TX_OPEN)
+ patm_tx_vcc_close(sc, vcc);
+ if (vcc->vflags & PATM_VCC_RX_OPEN)
+ patm_rx_vcc_close(sc, vcc);
+
+ if (vcc->vflags & PATM_VCC_ASYNC)
+ goto done;
+
+ while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) {
+ cv_wait(&sc->vcc_cv, &sc->mtx);
+ if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
+ /* ups, has been stopped */
+ error = EIO;
+ goto done;
+ }
+ }
+
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX))
+ patm_tx_vcc_closed(sc, vcc);
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NORX))
+ patm_rx_vcc_closed(sc, vcc);
+
+ patm_vcc_closed(sc, vcc);
+
+ done:
+ mtx_unlock(&sc->mtx);
+
+ return (error);
+}
+
+/*
+ * Close a VCC asynchronuosly
+ */
+static int
+patm_close_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph)
+{
+ struct atmio_closevcc v;
+
+ v.vpi = ATM_PH_VPI(&ph->aph);
+ v.vci = ATM_PH_VCI(&ph->aph);
+
+ return (patm_close_vcc(sc, &v));
+}
+
+/*
+ * VCC has been finally closed.
+ */
+void
+patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc)
+{
+
+#ifdef notyet
+ /* inform management about non-NG and NG-PVCs */
+ if (!(vcc->vcc.flags & ATMIO_FLAG_NG) ||
+ (vcc->vcc.flags & ATMIO_FLAG_PVC))
+ atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
+ (0 << 24) | (vcc->vcc.vpi << 16) | vcc->vcc.vci);
+#endif
+
+ sc->vccs_open--;
+ sc->vccs[vcc->cid] = NULL;
+ uma_zfree(sc->vcc_zone, vcc);
+}
+
+int
+patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct patm_softc *sc = ifp->if_softc;
+ int error = 0;
+ uint32_t cfg;
+ struct atmio_vcctable *vtab;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ mtx_lock(&sc->mtx);
+ ifp->if_flags |= IFF_UP;
+ if (!(ifp->if_flags & IFF_RUNNING))
+ patm_initialize(sc);
+ switch (ifa->ifa_addr->sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ case AF_INET6:
+ ifa->ifa_rtrequest = atm_rtrequest;
+ break;
+#endif
+ default:
+ break;
+ }
+ mtx_unlock(&sc->mtx);
+ break;
+
+ case SIOCSIFFLAGS:
+ mtx_lock(&sc->mtx);
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ patm_initialize(sc);
+ }
+ } else {
+ if (ifp->if_flags & IFF_RUNNING) {
+ patm_stop(sc);
+ }
+ }
+ mtx_unlock(&sc->mtx);
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
+
+ /*
+ * We need to toggle unassigned/idle cells ourself because
+ * the 77252 generates null cells for spacing. When switching
+ * null cells of it gets the timing wrong.
+ */
+ mtx_lock(&sc->mtx);
+ if (ifp->if_flags & IFF_RUNNING) {
+ if (sc->utopia.state & UTP_ST_UNASS) {
+ if (!(sc->flags & PATM_UNASS)) {
+ cfg = patm_nor_read(sc, IDT_NOR_CFG);
+ cfg &= ~IDT_CFG_IDLECLP;
+ patm_nor_write(sc, IDT_NOR_CFG, cfg);
+ sc->flags |= PATM_UNASS;
+ }
+ } else {
+ if (sc->flags & PATM_UNASS) {
+ cfg = patm_nor_read(sc, IDT_NOR_CFG);
+ cfg |= IDT_CFG_IDLECLP;
+ patm_nor_write(sc, IDT_NOR_CFG, cfg);
+ sc->flags &= ~PATM_UNASS;
+ }
+ }
+ } else {
+ if (sc->utopia.state & UTP_ST_UNASS)
+ sc->flags |= PATM_UNASS;
+ else
+ sc->flags &= ~PATM_UNASS;
+ }
+ mtx_unlock(&sc->mtx);
+ break;
+
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+ if (ifr->ifr_mtu > ATMMTU)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCATMOPENVCC: /* netgraph/harp internal use */
+ error = patm_open_vcc(sc, (struct atmio_openvcc *)data, 0);
+ break;
+
+ case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */
+ error = patm_close_vcc(sc, (struct atmio_closevcc *)data);
+ break;
+
+ case SIOCATMENA: /* NATM internal use */
+ error = patm_open_vcc1(sc, (struct atm_pseudoioctl *)data);
+ break;
+
+ case SIOCATMDIS: /* NATM internal use */
+ error = patm_close_vcc1(sc, (struct atm_pseudoioctl *)data);
+ break;
+
+ case SIOCATMGVCCS: /* external use */
+ /* return vcc table */
+ vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
+ sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1);
+ error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
+ vtab->count * sizeof(vtab->vccs[0]));
+ free(vtab, M_DEVBUF);
+ break;
+
+ case SIOCATMGETVCCS: /* netgraph internal use */
+ vtab = atm_getvccs((struct atmio_vcc **)sc->vccs,
+ sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0);
+ if (vtab == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ *(void **)data = vtab;
+ break;
+
+ default:
+ patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data);
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
OpenPOWER on IntegriCloud