if_patm_ioctl.c revision 118158
1/* 2 * Copyright (c) 2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * Author: Hartmut Brandt <harti@freebsd.org> 28 * 29 * Driver for IDT77252 based cards like ProSum's. 30 */ 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm_ioctl.c 118158 2003-07-29 13:21:57Z harti $"); 33 34#include "opt_inet.h" 35#include "opt_natm.h" 36 37#include <sys/types.h> 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/malloc.h> 41#include <sys/kernel.h> 42#include <sys/bus.h> 43#include <sys/errno.h> 44#include <sys/conf.h> 45#include <sys/module.h> 46#include <sys/lock.h> 47#include <sys/mutex.h> 48#include <sys/sysctl.h> 49#include <sys/queue.h> 50#include <sys/condvar.h> 51#include <vm/uma.h> 52 53#include <sys/sockio.h> 54#include <sys/mbuf.h> 55#include <sys/socket.h> 56 57#include <net/if.h> 58#include <net/if_media.h> 59#include <net/if_atm.h> 60#include <net/route.h> 61#include <netinet/in.h> 62#include <netinet/if_atm.h> 63 64#include <machine/bus.h> 65#include <machine/resource.h> 66#include <sys/bus.h> 67#include <sys/rman.h> 68#include <sys/mbpool.h> 69 70#include <dev/utopia/utopia.h> 71#include <dev/patm/idt77252reg.h> 72#include <dev/patm/if_patmvar.h> 73 74/* 75 * Open the VCC with the given parameters 76 */ 77static int 78patm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg, u_int async) 79{ 80 u_int cid; 81 struct patm_vcc *vcc; 82 int error = 0; 83 84 patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi, 85 arg->param.vci, arg->param.flags); 86 87 if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci)) 88 return (EINVAL); 89 if (arg->param.vci == 0 && (arg->param.vpi != 0 || 90 !(arg->param.flags & ATMIO_FLAG_NOTX) || 91 arg->param.aal != ATMIO_AAL_RAW)) 92 return (EINVAL); 93 cid = PATM_CID(sc, arg->param.vpi, arg->param.vci); 94 95 if ((arg->param.flags & ATMIO_FLAG_NOTX) && 96 (arg->param.flags & ATMIO_FLAG_NORX)) 97 return (EINVAL); 98 99 if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) && 100 (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX))) 101 return (EINVAL); 102 103 /* allocate vcc */ 104 vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO); 105 if (vcc == NULL) 106 return (ENOMEM); 107 108 mtx_lock(&sc->mtx); 109 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 110 /* stopped while we have analyzed the arguments */ 111 error = EIO; 112 goto done; 113 } 114 if (sc->vccs[cid] != NULL) { 115 /* ups, already open */ 116 error = EBUSY; 117 goto done; 118 } 119 120 /* check some parameters */ 121 vcc->cid = cid; 122 vcc->vcc = arg->param; 123 vcc->vflags = async; 124 vcc->rxhand = arg->rxhand; 125 switch (vcc->vcc.aal) { 126 127 case ATMIO_AAL_0: 128 case ATMIO_AAL_34: 129 case ATMIO_AAL_5: 130 break; 131 132 case ATMIO_AAL_RAW: 133 if (arg->param.vci == 0 && 134 !(arg->param.flags & ATMIO_FLAG_NOTX)) { 135 error = EINVAL; 136 goto done; 137 } 138 break; 139 140 default: 141 error = EINVAL; 142 goto done; 143 } 144 switch (vcc->vcc.traffic) { 145 146 case ATMIO_TRAFFIC_VBR: 147 case ATMIO_TRAFFIC_UBR: 148 case ATMIO_TRAFFIC_CBR: 149 case ATMIO_TRAFFIC_ABR: 150 break; 151 152 default: 153 error = EINVAL; 154 goto done; 155 } 156 157 /* initialize */ 158 vcc->chain = NULL; 159 vcc->last = NULL; 160 vcc->ibytes = vcc->ipackets = 0; 161 vcc->obytes = vcc->opackets = 0; 162 163 /* ask the TX and RX sides */ 164 patm_debug(sc, VCC, "Open VCC: asking Rx/Tx"); 165 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) && 166 (error = patm_tx_vcc_can_open(sc, vcc)) != 0) 167 goto done; 168 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) && 169 (error = patm_rx_vcc_can_open(sc, vcc)) != 0) 170 goto done; 171 172 /* ok - go ahead */ 173 sc->vccs[cid] = vcc; 174 175 patm_debug(sc, VCC, "Open VCC: opening"); 176 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 177 patm_tx_vcc_open(sc, vcc); 178 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 179 patm_rx_vcc_open(sc, vcc); 180 181 /* inform management about non-NG and NG-PVCs */ 182 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 183 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 184 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, vcc->vcc.vpi, 185 vcc->vcc.vci, 1); 186 187 patm_debug(sc, VCC, "Open VCC: now open"); 188 189 /* don't free below */ 190 vcc = NULL; 191 192 sc->vccs_open++; 193 194 /* done */ 195 done: 196 mtx_unlock(&sc->mtx); 197 if (vcc != NULL) 198 uma_zfree(sc->vcc_zone, vcc); 199 return (error); 200} 201 202/* 203 * Enable ioctl for NATM. Map to an open ioctl. 204 */ 205static int 206patm_open_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph) 207{ 208 struct atmio_openvcc v; 209 210 bzero(&v, sizeof(v)); 211 v.param.flags = ATM_PH_FLAGS(&ph->aph) & (ATM_PH_AAL5 | ATM_PH_LLCSNAP); 212 v.param.vpi = ATM_PH_VPI(&ph->aph); 213 v.param.vci = ATM_PH_VCI(&ph->aph); 214 v.param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5) 215 ? ATMIO_AAL_5 : ATMIO_AAL_0; 216 v.param.traffic = ATMIO_TRAFFIC_UBR;; 217 v.param.tparam.pcr = sc->ifatm.mib.pcr; 218 v.rxhand = ph->rxhand; 219 220 return (patm_open_vcc(sc, &v, PATM_VCC_ASYNC)); 221} 222 223/* 224 * Try to close the given VCC 225 */ 226static int 227patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg) 228{ 229 u_int cid; 230 struct patm_vcc *vcc; 231 int error = 0; 232 233 patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci); 234 235 if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci)) 236 return (EINVAL); 237 cid = PATM_CID(sc, arg->vpi, arg->vci); 238 239 mtx_lock(&sc->mtx); 240 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 241 /* stopped while we have analyzed the arguments */ 242 error = EIO; 243 goto done; 244 } 245 246 vcc = sc->vccs[cid]; 247 if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) { 248 error = ENOENT; 249 goto done; 250 } 251 252 if (vcc->vflags & PATM_VCC_TX_OPEN) 253 patm_tx_vcc_close(sc, vcc); 254 if (vcc->vflags & PATM_VCC_RX_OPEN) 255 patm_rx_vcc_close(sc, vcc); 256 257 if (vcc->vflags & PATM_VCC_ASYNC) 258 goto done; 259 260 while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) { 261 cv_wait(&sc->vcc_cv, &sc->mtx); 262 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 263 /* ups, has been stopped */ 264 error = EIO; 265 goto done; 266 } 267 } 268 269 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 270 patm_tx_vcc_closed(sc, vcc); 271 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 272 patm_rx_vcc_closed(sc, vcc); 273 274 patm_vcc_closed(sc, vcc); 275 276 done: 277 mtx_unlock(&sc->mtx); 278 279 return (error); 280} 281 282/* 283 * Close a VCC asynchronuosly 284 */ 285static int 286patm_close_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph) 287{ 288 struct atmio_closevcc v; 289 290 v.vpi = ATM_PH_VPI(&ph->aph); 291 v.vci = ATM_PH_VCI(&ph->aph); 292 293 return (patm_close_vcc(sc, &v)); 294} 295 296/* 297 * VCC has been finally closed. 298 */ 299void 300patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc) 301{ 302 303 /* inform management about non-NG and NG-PVCs */ 304 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 305 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 306 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, vcc->vcc.vpi, 307 vcc->vcc.vci, 0); 308 309 sc->vccs_open--; 310 sc->vccs[vcc->cid] = NULL; 311 uma_zfree(sc->vcc_zone, vcc); 312} 313 314int 315patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 316{ 317 struct ifreq *ifr = (struct ifreq *)data; 318 struct ifaddr *ifa = (struct ifaddr *)data; 319 struct patm_softc *sc = ifp->if_softc; 320 int error = 0; 321 uint32_t cfg; 322 struct atmio_vcctable *vtab; 323 324 switch (cmd) { 325 326 case SIOCSIFADDR: 327 mtx_lock(&sc->mtx); 328 ifp->if_flags |= IFF_UP; 329 if (!(ifp->if_flags & IFF_RUNNING)) 330 patm_initialize(sc); 331 switch (ifa->ifa_addr->sa_family) { 332 333#ifdef INET 334 case AF_INET: 335 case AF_INET6: 336 ifa->ifa_rtrequest = atm_rtrequest; 337 break; 338#endif 339 default: 340 break; 341 } 342 mtx_unlock(&sc->mtx); 343 break; 344 345 case SIOCSIFFLAGS: 346 mtx_lock(&sc->mtx); 347 if (ifp->if_flags & IFF_UP) { 348 if (!(ifp->if_flags & IFF_RUNNING)) { 349 patm_initialize(sc); 350 } 351 } else { 352 if (ifp->if_flags & IFF_RUNNING) { 353 patm_stop(sc); 354 } 355 } 356 mtx_unlock(&sc->mtx); 357 break; 358 359 case SIOCGIFMEDIA: 360 case SIOCSIFMEDIA: 361 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 362 363 /* 364 * We need to toggle unassigned/idle cells ourself because 365 * the 77252 generates null cells for spacing. When switching 366 * null cells of it gets the timing wrong. 367 */ 368 mtx_lock(&sc->mtx); 369 if (ifp->if_flags & IFF_RUNNING) { 370 if (sc->utopia.state & UTP_ST_UNASS) { 371 if (!(sc->flags & PATM_UNASS)) { 372 cfg = patm_nor_read(sc, IDT_NOR_CFG); 373 cfg &= ~IDT_CFG_IDLECLP; 374 patm_nor_write(sc, IDT_NOR_CFG, cfg); 375 sc->flags |= PATM_UNASS; 376 } 377 } else { 378 if (sc->flags & PATM_UNASS) { 379 cfg = patm_nor_read(sc, IDT_NOR_CFG); 380 cfg |= IDT_CFG_IDLECLP; 381 patm_nor_write(sc, IDT_NOR_CFG, cfg); 382 sc->flags &= ~PATM_UNASS; 383 } 384 } 385 } else { 386 if (sc->utopia.state & UTP_ST_UNASS) 387 sc->flags |= PATM_UNASS; 388 else 389 sc->flags &= ~PATM_UNASS; 390 } 391 mtx_unlock(&sc->mtx); 392 break; 393 394 case SIOCSIFMTU: 395 /* 396 * Set the interface MTU. 397 */ 398 if (ifr->ifr_mtu > ATMMTU) 399 error = EINVAL; 400 else 401 ifp->if_mtu = ifr->ifr_mtu; 402 break; 403 404 case SIOCATMOPENVCC: /* netgraph/harp internal use */ 405 error = patm_open_vcc(sc, (struct atmio_openvcc *)data, 0); 406 break; 407 408 case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */ 409 error = patm_close_vcc(sc, (struct atmio_closevcc *)data); 410 break; 411 412 case SIOCATMENA: /* NATM internal use */ 413 error = patm_open_vcc1(sc, (struct atm_pseudoioctl *)data); 414 break; 415 416 case SIOCATMDIS: /* NATM internal use */ 417 error = patm_close_vcc1(sc, (struct atm_pseudoioctl *)data); 418 break; 419 420 case SIOCATMGVCCS: /* external use */ 421 /* return vcc table */ 422 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 423 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1); 424 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) + 425 vtab->count * sizeof(vtab->vccs[0])); 426 free(vtab, M_DEVBUF); 427 break; 428 429 case SIOCATMGETVCCS: /* netgraph internal use */ 430 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 431 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0); 432 if (vtab == NULL) { 433 error = ENOMEM; 434 break; 435 } 436 *(void **)data = vtab; 437 break; 438 439 default: 440 patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data); 441 error = EINVAL; 442 break; 443 } 444 445 return (error); 446} 447