1139749Simp/*- 2117632Sharti * Copyright (c) 2003 3117632Sharti * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4117632Sharti * All rights reserved. 5117632Sharti * 6117632Sharti * Redistribution and use in source and binary forms, with or without 7117632Sharti * modification, are permitted provided that the following conditions 8117632Sharti * are met: 9117632Sharti * 1. Redistributions of source code must retain the above copyright 10117632Sharti * notice, this list of conditions and the following disclaimer. 11117632Sharti * 2. Redistributions in binary form must reproduce the above copyright 12117632Sharti * notice, this list of conditions and the following disclaimer in the 13117632Sharti * documentation and/or other materials provided with the distribution. 14117632Sharti * 15117632Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16117632Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17117632Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18117632Sharti * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19117632Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20117632Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21117632Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22117632Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23117632Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24117632Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25117632Sharti * SUCH DAMAGE. 26117632Sharti * 27117632Sharti * Author: Hartmut Brandt <harti@freebsd.org> 28117632Sharti * 29117632Sharti * Driver for IDT77252 based cards like ProSum's. 30117632Sharti */ 31119418Sobrien 32117632Sharti#include <sys/cdefs.h> 33117632Sharti__FBSDID("$FreeBSD$"); 34117632Sharti 35117632Sharti#include "opt_inet.h" 36117632Sharti#include "opt_natm.h" 37117632Sharti 38117632Sharti#include <sys/types.h> 39117632Sharti#include <sys/param.h> 40117632Sharti#include <sys/systm.h> 41117632Sharti#include <sys/malloc.h> 42117632Sharti#include <sys/kernel.h> 43117632Sharti#include <sys/bus.h> 44117632Sharti#include <sys/errno.h> 45117632Sharti#include <sys/conf.h> 46117632Sharti#include <sys/module.h> 47117632Sharti#include <sys/lock.h> 48117632Sharti#include <sys/mutex.h> 49117632Sharti#include <sys/sysctl.h> 50117632Sharti#include <sys/queue.h> 51117632Sharti#include <sys/condvar.h> 52117632Sharti#include <vm/uma.h> 53117632Sharti 54117632Sharti#include <sys/sockio.h> 55117632Sharti#include <sys/mbuf.h> 56117632Sharti#include <sys/socket.h> 57117632Sharti 58117632Sharti#include <net/if.h> 59117632Sharti#include <net/if_media.h> 60117632Sharti#include <net/if_atm.h> 61117632Sharti#include <net/route.h> 62117632Sharti#include <netinet/in.h> 63117632Sharti#include <netinet/if_atm.h> 64117632Sharti 65117632Sharti#include <machine/bus.h> 66117632Sharti#include <machine/resource.h> 67117632Sharti#include <sys/bus.h> 68117632Sharti#include <sys/rman.h> 69117632Sharti#include <sys/mbpool.h> 70117632Sharti 71117632Sharti#include <dev/utopia/utopia.h> 72117632Sharti#include <dev/patm/idt77252reg.h> 73117632Sharti#include <dev/patm/if_patmvar.h> 74117632Sharti 75117632Sharti/* 76117632Sharti * Open the VCC with the given parameters 77117632Sharti */ 78117632Shartistatic int 79118539Shartipatm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg) 80117632Sharti{ 81117632Sharti u_int cid; 82117632Sharti struct patm_vcc *vcc; 83117632Sharti int error = 0; 84117632Sharti 85117632Sharti patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi, 86117632Sharti arg->param.vci, arg->param.flags); 87117632Sharti 88117632Sharti if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci)) 89117632Sharti return (EINVAL); 90117632Sharti if (arg->param.vci == 0 && (arg->param.vpi != 0 || 91117632Sharti !(arg->param.flags & ATMIO_FLAG_NOTX) || 92117632Sharti arg->param.aal != ATMIO_AAL_RAW)) 93117632Sharti return (EINVAL); 94117632Sharti cid = PATM_CID(sc, arg->param.vpi, arg->param.vci); 95117632Sharti 96117632Sharti if ((arg->param.flags & ATMIO_FLAG_NOTX) && 97117632Sharti (arg->param.flags & ATMIO_FLAG_NORX)) 98117632Sharti return (EINVAL); 99117632Sharti 100117632Sharti if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) && 101117632Sharti (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX))) 102117632Sharti return (EINVAL); 103117632Sharti 104117632Sharti /* allocate vcc */ 105117632Sharti vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO); 106117632Sharti if (vcc == NULL) 107117632Sharti return (ENOMEM); 108117632Sharti 109117632Sharti mtx_lock(&sc->mtx); 110148887Srwatson if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { 111117632Sharti /* stopped while we have analyzed the arguments */ 112117632Sharti error = EIO; 113117632Sharti goto done; 114117632Sharti } 115117632Sharti if (sc->vccs[cid] != NULL) { 116117632Sharti /* ups, already open */ 117117632Sharti error = EBUSY; 118117632Sharti goto done; 119117632Sharti } 120117632Sharti 121117632Sharti /* check some parameters */ 122117632Sharti vcc->cid = cid; 123117632Sharti vcc->vcc = arg->param; 124118539Sharti vcc->vflags = 0; 125117632Sharti vcc->rxhand = arg->rxhand; 126117632Sharti switch (vcc->vcc.aal) { 127117632Sharti 128117632Sharti case ATMIO_AAL_0: 129117632Sharti case ATMIO_AAL_34: 130117632Sharti case ATMIO_AAL_5: 131117632Sharti break; 132117632Sharti 133117632Sharti case ATMIO_AAL_RAW: 134117632Sharti if (arg->param.vci == 0 && 135117632Sharti !(arg->param.flags & ATMIO_FLAG_NOTX)) { 136117632Sharti error = EINVAL; 137117632Sharti goto done; 138117632Sharti } 139117632Sharti break; 140117632Sharti 141117632Sharti default: 142117632Sharti error = EINVAL; 143117632Sharti goto done; 144117632Sharti } 145117632Sharti switch (vcc->vcc.traffic) { 146117632Sharti 147117632Sharti case ATMIO_TRAFFIC_VBR: 148117632Sharti case ATMIO_TRAFFIC_UBR: 149117632Sharti case ATMIO_TRAFFIC_CBR: 150117632Sharti case ATMIO_TRAFFIC_ABR: 151117632Sharti break; 152117632Sharti 153117632Sharti default: 154117632Sharti error = EINVAL; 155117632Sharti goto done; 156117632Sharti } 157117632Sharti 158117632Sharti /* initialize */ 159117632Sharti vcc->chain = NULL; 160117632Sharti vcc->last = NULL; 161117632Sharti vcc->ibytes = vcc->ipackets = 0; 162117632Sharti vcc->obytes = vcc->opackets = 0; 163117632Sharti 164117632Sharti /* ask the TX and RX sides */ 165117632Sharti patm_debug(sc, VCC, "Open VCC: asking Rx/Tx"); 166117632Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) && 167117632Sharti (error = patm_tx_vcc_can_open(sc, vcc)) != 0) 168117632Sharti goto done; 169117632Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) && 170117632Sharti (error = patm_rx_vcc_can_open(sc, vcc)) != 0) 171117632Sharti goto done; 172117632Sharti 173117632Sharti /* ok - go ahead */ 174117632Sharti sc->vccs[cid] = vcc; 175118601Sharti patm_load_vc(sc, vcc, 0); 176117632Sharti 177117632Sharti /* don't free below */ 178117632Sharti vcc = NULL; 179117632Sharti sc->vccs_open++; 180117632Sharti 181117632Sharti /* done */ 182117632Sharti done: 183117632Sharti mtx_unlock(&sc->mtx); 184117632Sharti if (vcc != NULL) 185117632Sharti uma_zfree(sc->vcc_zone, vcc); 186117632Sharti return (error); 187117632Sharti} 188117632Sharti 189118601Shartivoid 190118601Shartipatm_load_vc(struct patm_softc *sc, struct patm_vcc *vcc, int reload) 191118601Sharti{ 192118601Sharti 193118601Sharti patm_debug(sc, VCC, "Open VCC: opening"); 194118601Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 195118601Sharti patm_tx_vcc_open(sc, vcc); 196118601Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 197118601Sharti patm_rx_vcc_open(sc, vcc); 198118601Sharti 199118601Sharti if (!reload) { 200118601Sharti /* inform management about non-NG and NG-PVCs */ 201118601Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 202118601Sharti (vcc->vcc.flags & ATMIO_FLAG_PVC)) 203147256Sbrooks ATMEV_SEND_VCC_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi, 204118601Sharti vcc->vcc.vci, 1); 205118601Sharti } 206118601Sharti 207118601Sharti patm_debug(sc, VCC, "Open VCC: now open"); 208118601Sharti} 209118601Sharti 210117632Sharti/* 211117632Sharti * Try to close the given VCC 212117632Sharti */ 213117632Shartistatic int 214117632Shartipatm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg) 215117632Sharti{ 216117632Sharti u_int cid; 217117632Sharti struct patm_vcc *vcc; 218117632Sharti int error = 0; 219117632Sharti 220117632Sharti patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci); 221117632Sharti 222117632Sharti if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci)) 223117632Sharti return (EINVAL); 224117632Sharti cid = PATM_CID(sc, arg->vpi, arg->vci); 225117632Sharti 226117632Sharti mtx_lock(&sc->mtx); 227148887Srwatson if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { 228117632Sharti /* stopped while we have analyzed the arguments */ 229117632Sharti error = EIO; 230117632Sharti goto done; 231117632Sharti } 232117632Sharti 233117632Sharti vcc = sc->vccs[cid]; 234117632Sharti if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) { 235117632Sharti error = ENOENT; 236117632Sharti goto done; 237117632Sharti } 238117632Sharti 239117632Sharti if (vcc->vflags & PATM_VCC_TX_OPEN) 240117632Sharti patm_tx_vcc_close(sc, vcc); 241117632Sharti if (vcc->vflags & PATM_VCC_RX_OPEN) 242117632Sharti patm_rx_vcc_close(sc, vcc); 243117632Sharti 244118539Sharti if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) 245117632Sharti goto done; 246117632Sharti 247117632Sharti while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) { 248117632Sharti cv_wait(&sc->vcc_cv, &sc->mtx); 249148887Srwatson if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { 250117632Sharti /* ups, has been stopped */ 251117632Sharti error = EIO; 252117632Sharti goto done; 253117632Sharti } 254117632Sharti } 255117632Sharti 256117632Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 257117632Sharti patm_tx_vcc_closed(sc, vcc); 258117632Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 259117632Sharti patm_rx_vcc_closed(sc, vcc); 260117632Sharti 261117632Sharti patm_vcc_closed(sc, vcc); 262117632Sharti 263117632Sharti done: 264117632Sharti mtx_unlock(&sc->mtx); 265117632Sharti 266117632Sharti return (error); 267117632Sharti} 268117632Sharti 269117632Sharti/* 270117632Sharti * VCC has been finally closed. 271117632Sharti */ 272117632Shartivoid 273117632Shartipatm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc) 274117632Sharti{ 275117632Sharti 276117632Sharti /* inform management about non-NG and NG-PVCs */ 277117632Sharti if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 278117632Sharti (vcc->vcc.flags & ATMIO_FLAG_PVC)) 279147256Sbrooks ATMEV_SEND_VCC_CHANGED(IFP2IFATM(sc->ifp), vcc->vcc.vpi, 280118158Sharti vcc->vcc.vci, 0); 281117632Sharti 282117632Sharti sc->vccs_open--; 283117632Sharti sc->vccs[vcc->cid] = NULL; 284117632Sharti uma_zfree(sc->vcc_zone, vcc); 285117632Sharti} 286117632Sharti 287117632Shartiint 288117632Shartipatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 289117632Sharti{ 290117632Sharti struct ifreq *ifr = (struct ifreq *)data; 291117632Sharti struct ifaddr *ifa = (struct ifaddr *)data; 292117632Sharti struct patm_softc *sc = ifp->if_softc; 293117632Sharti int error = 0; 294117632Sharti uint32_t cfg; 295117632Sharti struct atmio_vcctable *vtab; 296117632Sharti 297117632Sharti switch (cmd) { 298117632Sharti 299117632Sharti case SIOCSIFADDR: 300117632Sharti mtx_lock(&sc->mtx); 301117632Sharti ifp->if_flags |= IFF_UP; 302148887Srwatson if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 303117632Sharti patm_initialize(sc); 304117632Sharti switch (ifa->ifa_addr->sa_family) { 305117632Sharti 306117632Sharti#ifdef INET 307117632Sharti case AF_INET: 308117632Sharti case AF_INET6: 309117632Sharti ifa->ifa_rtrequest = atm_rtrequest; 310117632Sharti break; 311117632Sharti#endif 312117632Sharti default: 313117632Sharti break; 314117632Sharti } 315117632Sharti mtx_unlock(&sc->mtx); 316117632Sharti break; 317117632Sharti 318117632Sharti case SIOCSIFFLAGS: 319117632Sharti mtx_lock(&sc->mtx); 320117632Sharti if (ifp->if_flags & IFF_UP) { 321148887Srwatson if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 322117632Sharti patm_initialize(sc); 323117632Sharti } 324117632Sharti } else { 325148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 326117632Sharti patm_stop(sc); 327117632Sharti } 328117632Sharti } 329117632Sharti mtx_unlock(&sc->mtx); 330117632Sharti break; 331117632Sharti 332117632Sharti case SIOCGIFMEDIA: 333117632Sharti case SIOCSIFMEDIA: 334117632Sharti error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 335117632Sharti 336117632Sharti /* 337117632Sharti * We need to toggle unassigned/idle cells ourself because 338117632Sharti * the 77252 generates null cells for spacing. When switching 339117632Sharti * null cells of it gets the timing wrong. 340117632Sharti */ 341117632Sharti mtx_lock(&sc->mtx); 342148887Srwatson if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 343117632Sharti if (sc->utopia.state & UTP_ST_UNASS) { 344117632Sharti if (!(sc->flags & PATM_UNASS)) { 345117632Sharti cfg = patm_nor_read(sc, IDT_NOR_CFG); 346117632Sharti cfg &= ~IDT_CFG_IDLECLP; 347117632Sharti patm_nor_write(sc, IDT_NOR_CFG, cfg); 348117632Sharti sc->flags |= PATM_UNASS; 349117632Sharti } 350117632Sharti } else { 351117632Sharti if (sc->flags & PATM_UNASS) { 352117632Sharti cfg = patm_nor_read(sc, IDT_NOR_CFG); 353117632Sharti cfg |= IDT_CFG_IDLECLP; 354117632Sharti patm_nor_write(sc, IDT_NOR_CFG, cfg); 355117632Sharti sc->flags &= ~PATM_UNASS; 356117632Sharti } 357117632Sharti } 358117632Sharti } else { 359117632Sharti if (sc->utopia.state & UTP_ST_UNASS) 360117632Sharti sc->flags |= PATM_UNASS; 361117632Sharti else 362117632Sharti sc->flags &= ~PATM_UNASS; 363117632Sharti } 364117632Sharti mtx_unlock(&sc->mtx); 365117632Sharti break; 366117632Sharti 367117632Sharti case SIOCSIFMTU: 368117632Sharti /* 369117632Sharti * Set the interface MTU. 370117632Sharti */ 371117632Sharti if (ifr->ifr_mtu > ATMMTU) 372117632Sharti error = EINVAL; 373117632Sharti else 374117632Sharti ifp->if_mtu = ifr->ifr_mtu; 375117632Sharti break; 376117632Sharti 377118548Sharti case SIOCATMOPENVCC: /* kernel internal use */ 378118539Sharti error = patm_open_vcc(sc, (struct atmio_openvcc *)data); 379117632Sharti break; 380117632Sharti 381118548Sharti case SIOCATMCLOSEVCC: /* kernel internal use */ 382117632Sharti error = patm_close_vcc(sc, (struct atmio_closevcc *)data); 383117632Sharti break; 384117632Sharti 385117632Sharti case SIOCATMGVCCS: /* external use */ 386117632Sharti /* return vcc table */ 387117632Sharti vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 388117632Sharti sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1); 389117632Sharti error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) + 390117632Sharti vtab->count * sizeof(vtab->vccs[0])); 391117632Sharti free(vtab, M_DEVBUF); 392117632Sharti break; 393117632Sharti 394117632Sharti case SIOCATMGETVCCS: /* netgraph internal use */ 395117632Sharti vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 396117632Sharti sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0); 397117632Sharti if (vtab == NULL) { 398117632Sharti error = ENOMEM; 399117632Sharti break; 400117632Sharti } 401117632Sharti *(void **)data = vtab; 402117632Sharti break; 403117632Sharti 404117632Sharti default: 405117632Sharti patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data); 406117632Sharti error = EINVAL; 407117632Sharti break; 408117632Sharti } 409117632Sharti 410117632Sharti return (error); 411117632Sharti} 412