if_patm_ioctl.c revision 117632
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 117632 2003-07-15 11:57:24Z 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#ifdef notyet 182 /* inform management about non-NG and NG-PVCs */ 183 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 184 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 185 atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED, 186 (1 << 24) | (vcc->vcc.vpi << 16) | vcc->vcc.vci); 187#endif 188 189 patm_debug(sc, VCC, "Open VCC: now open"); 190 191 /* don't free below */ 192 vcc = NULL; 193 194 sc->vccs_open++; 195 196 /* done */ 197 done: 198 mtx_unlock(&sc->mtx); 199 if (vcc != NULL) 200 uma_zfree(sc->vcc_zone, vcc); 201 return (error); 202} 203 204/* 205 * Enable ioctl for NATM. Map to an open ioctl. 206 */ 207static int 208patm_open_vcc1(struct patm_softc *sc, struct atm_pseudoioctl *ph) 209{ 210 struct atmio_openvcc v; 211 212 bzero(&v, sizeof(v)); 213 v.param.flags = ATM_PH_FLAGS(&ph->aph) & (ATM_PH_AAL5 | ATM_PH_LLCSNAP); 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, PATM_VCC_ASYNC)); 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->vflags & PATM_VCC_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#ifdef notyet 306 /* inform management about non-NG and NG-PVCs */ 307 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 308 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 309 atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED, 310 (0 << 24) | (vcc->vcc.vpi << 16) | vcc->vcc.vci); 311#endif 312 313 sc->vccs_open--; 314 sc->vccs[vcc->cid] = NULL; 315 uma_zfree(sc->vcc_zone, vcc); 316} 317 318int 319patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 320{ 321 struct ifreq *ifr = (struct ifreq *)data; 322 struct ifaddr *ifa = (struct ifaddr *)data; 323 struct patm_softc *sc = ifp->if_softc; 324 int error = 0; 325 uint32_t cfg; 326 struct atmio_vcctable *vtab; 327 328 switch (cmd) { 329 330 case SIOCSIFADDR: 331 mtx_lock(&sc->mtx); 332 ifp->if_flags |= IFF_UP; 333 if (!(ifp->if_flags & IFF_RUNNING)) 334 patm_initialize(sc); 335 switch (ifa->ifa_addr->sa_family) { 336 337#ifdef INET 338 case AF_INET: 339 case AF_INET6: 340 ifa->ifa_rtrequest = atm_rtrequest; 341 break; 342#endif 343 default: 344 break; 345 } 346 mtx_unlock(&sc->mtx); 347 break; 348 349 case SIOCSIFFLAGS: 350 mtx_lock(&sc->mtx); 351 if (ifp->if_flags & IFF_UP) { 352 if (!(ifp->if_flags & IFF_RUNNING)) { 353 patm_initialize(sc); 354 } 355 } else { 356 if (ifp->if_flags & IFF_RUNNING) { 357 patm_stop(sc); 358 } 359 } 360 mtx_unlock(&sc->mtx); 361 break; 362 363 case SIOCGIFMEDIA: 364 case SIOCSIFMEDIA: 365 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 366 367 /* 368 * We need to toggle unassigned/idle cells ourself because 369 * the 77252 generates null cells for spacing. When switching 370 * null cells of it gets the timing wrong. 371 */ 372 mtx_lock(&sc->mtx); 373 if (ifp->if_flags & IFF_RUNNING) { 374 if (sc->utopia.state & UTP_ST_UNASS) { 375 if (!(sc->flags & PATM_UNASS)) { 376 cfg = patm_nor_read(sc, IDT_NOR_CFG); 377 cfg &= ~IDT_CFG_IDLECLP; 378 patm_nor_write(sc, IDT_NOR_CFG, cfg); 379 sc->flags |= PATM_UNASS; 380 } 381 } else { 382 if (sc->flags & PATM_UNASS) { 383 cfg = patm_nor_read(sc, IDT_NOR_CFG); 384 cfg |= IDT_CFG_IDLECLP; 385 patm_nor_write(sc, IDT_NOR_CFG, cfg); 386 sc->flags &= ~PATM_UNASS; 387 } 388 } 389 } else { 390 if (sc->utopia.state & UTP_ST_UNASS) 391 sc->flags |= PATM_UNASS; 392 else 393 sc->flags &= ~PATM_UNASS; 394 } 395 mtx_unlock(&sc->mtx); 396 break; 397 398 case SIOCSIFMTU: 399 /* 400 * Set the interface MTU. 401 */ 402 if (ifr->ifr_mtu > ATMMTU) 403 error = EINVAL; 404 else 405 ifp->if_mtu = ifr->ifr_mtu; 406 break; 407 408 case SIOCATMOPENVCC: /* netgraph/harp internal use */ 409 error = patm_open_vcc(sc, (struct atmio_openvcc *)data, 0); 410 break; 411 412 case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */ 413 error = patm_close_vcc(sc, (struct atmio_closevcc *)data); 414 break; 415 416 case SIOCATMENA: /* NATM internal use */ 417 error = patm_open_vcc1(sc, (struct atm_pseudoioctl *)data); 418 break; 419 420 case SIOCATMDIS: /* NATM internal use */ 421 error = patm_close_vcc1(sc, (struct atm_pseudoioctl *)data); 422 break; 423 424 case SIOCATMGVCCS: /* external use */ 425 /* return vcc table */ 426 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 427 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1); 428 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) + 429 vtab->count * sizeof(vtab->vccs[0])); 430 free(vtab, M_DEVBUF); 431 break; 432 433 case SIOCATMGETVCCS: /* netgraph internal use */ 434 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 435 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0); 436 if (vtab == NULL) { 437 error = ENOMEM; 438 break; 439 } 440 *(void **)data = vtab; 441 break; 442 443 default: 444 patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data); 445 error = EINVAL; 446 break; 447 } 448 449 return (error); 450} 451