if_patm_ioctl.c revision 118539
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 118539 2003-08-06 13:09:36Z 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) 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 = 0; 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.flags |= ATMIO_FLAG_ASYNC; 213 214 v.param.vpi = ATM_PH_VPI(&ph->aph); 215 v.param.vci = ATM_PH_VCI(&ph->aph); 216 v.param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5) 217 ? ATMIO_AAL_5 : ATMIO_AAL_0; 218 v.param.traffic = ATMIO_TRAFFIC_UBR;; 219 v.param.tparam.pcr = sc->ifatm.mib.pcr; 220 v.rxhand = ph->rxhand; 221 222 return (patm_open_vcc(sc, &v)); 223} 224 225/* 226 * Try to close the given VCC 227 */ 228static int 229patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg) 230{ 231 u_int cid; 232 struct patm_vcc *vcc; 233 int error = 0; 234 235 patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci); 236 237 if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci)) 238 return (EINVAL); 239 cid = PATM_CID(sc, arg->vpi, arg->vci); 240 241 mtx_lock(&sc->mtx); 242 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 243 /* stopped while we have analyzed the arguments */ 244 error = EIO; 245 goto done; 246 } 247 248 vcc = sc->vccs[cid]; 249 if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) { 250 error = ENOENT; 251 goto done; 252 } 253 254 if (vcc->vflags & PATM_VCC_TX_OPEN) 255 patm_tx_vcc_close(sc, vcc); 256 if (vcc->vflags & PATM_VCC_RX_OPEN) 257 patm_rx_vcc_close(sc, vcc); 258 259 if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) 260 goto done; 261 262 while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) { 263 cv_wait(&sc->vcc_cv, &sc->mtx); 264 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 265 /* ups, has been stopped */ 266 error = EIO; 267 goto done; 268 } 269 } 270 271 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 272 patm_tx_vcc_closed(sc, vcc); 273 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 274 patm_rx_vcc_closed(sc, vcc); 275 276 patm_vcc_closed(sc, vcc); 277 278 done: 279 mtx_unlock(&sc->mtx); 280 281 return (error); 282} 283 284/* 285 * Close a VCC asynchronuosly 286 */ 287static int 288patm_close_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph) 289{ 290 struct atmio_closevcc v; 291 292 v.vpi = ATM_PH_VPI(&ph->aph); 293 v.vci = ATM_PH_VCI(&ph->aph); 294 295 return (patm_close_vcc(sc, &v)); 296} 297 298/* 299 * VCC has been finally closed. 300 */ 301void 302patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc) 303{ 304 305 /* inform management about non-NG and NG-PVCs */ 306 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 307 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 308 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, vcc->vcc.vpi, 309 vcc->vcc.vci, 0); 310 311 sc->vccs_open--; 312 sc->vccs[vcc->cid] = NULL; 313 uma_zfree(sc->vcc_zone, vcc); 314} 315 316int 317patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 318{ 319 struct ifreq *ifr = (struct ifreq *)data; 320 struct ifaddr *ifa = (struct ifaddr *)data; 321 struct patm_softc *sc = ifp->if_softc; 322 int error = 0; 323 uint32_t cfg; 324 struct atmio_vcctable *vtab; 325 326 switch (cmd) { 327 328 case SIOCSIFADDR: 329 mtx_lock(&sc->mtx); 330 ifp->if_flags |= IFF_UP; 331 if (!(ifp->if_flags & IFF_RUNNING)) 332 patm_initialize(sc); 333 switch (ifa->ifa_addr->sa_family) { 334 335#ifdef INET 336 case AF_INET: 337 case AF_INET6: 338 ifa->ifa_rtrequest = atm_rtrequest; 339 break; 340#endif 341 default: 342 break; 343 } 344 mtx_unlock(&sc->mtx); 345 break; 346 347 case SIOCSIFFLAGS: 348 mtx_lock(&sc->mtx); 349 if (ifp->if_flags & IFF_UP) { 350 if (!(ifp->if_flags & IFF_RUNNING)) { 351 patm_initialize(sc); 352 } 353 } else { 354 if (ifp->if_flags & IFF_RUNNING) { 355 patm_stop(sc); 356 } 357 } 358 mtx_unlock(&sc->mtx); 359 break; 360 361 case SIOCGIFMEDIA: 362 case SIOCSIFMEDIA: 363 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 364 365 /* 366 * We need to toggle unassigned/idle cells ourself because 367 * the 77252 generates null cells for spacing. When switching 368 * null cells of it gets the timing wrong. 369 */ 370 mtx_lock(&sc->mtx); 371 if (ifp->if_flags & IFF_RUNNING) { 372 if (sc->utopia.state & UTP_ST_UNASS) { 373 if (!(sc->flags & PATM_UNASS)) { 374 cfg = patm_nor_read(sc, IDT_NOR_CFG); 375 cfg &= ~IDT_CFG_IDLECLP; 376 patm_nor_write(sc, IDT_NOR_CFG, cfg); 377 sc->flags |= PATM_UNASS; 378 } 379 } else { 380 if (sc->flags & PATM_UNASS) { 381 cfg = patm_nor_read(sc, IDT_NOR_CFG); 382 cfg |= IDT_CFG_IDLECLP; 383 patm_nor_write(sc, IDT_NOR_CFG, cfg); 384 sc->flags &= ~PATM_UNASS; 385 } 386 } 387 } else { 388 if (sc->utopia.state & UTP_ST_UNASS) 389 sc->flags |= PATM_UNASS; 390 else 391 sc->flags &= ~PATM_UNASS; 392 } 393 mtx_unlock(&sc->mtx); 394 break; 395 396 case SIOCSIFMTU: 397 /* 398 * Set the interface MTU. 399 */ 400 if (ifr->ifr_mtu > ATMMTU) 401 error = EINVAL; 402 else 403 ifp->if_mtu = ifr->ifr_mtu; 404 break; 405 406 case SIOCATMOPENVCC: /* netgraph/harp internal use */ 407 error = patm_open_vcc(sc, (struct atmio_openvcc *)data); 408 break; 409 410 case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */ 411 error = patm_close_vcc(sc, (struct atmio_closevcc *)data); 412 break; 413 414 case SIOCATMENA: /* NATM internal use */ 415 error = patm_open_vcc1(sc, (struct atm_pseudoioctl *)data); 416 break; 417 418 case SIOCATMDIS: /* NATM internal use */ 419 error = patm_close_vcc1(sc, (struct atm_pseudoioctl *)data); 420 break; 421 422 case SIOCATMGVCCS: /* external use */ 423 /* return vcc table */ 424 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 425 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1); 426 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) + 427 vtab->count * sizeof(vtab->vccs[0])); 428 free(vtab, M_DEVBUF); 429 break; 430 431 case SIOCATMGETVCCS: /* netgraph internal use */ 432 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 433 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0); 434 if (vtab == NULL) { 435 error = ENOMEM; 436 break; 437 } 438 *(void **)data = vtab; 439 break; 440 441 default: 442 patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data); 443 error = EINVAL; 444 break; 445 } 446 447 return (error); 448} 449