if_patm_ioctl.c revision 139749
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 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/dev/patm/if_patm_ioctl.c 139749 2005-01-06 01:43:34Z imp $"); 34 35#include "opt_inet.h" 36#include "opt_natm.h" 37 38#include <sys/types.h> 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/malloc.h> 42#include <sys/kernel.h> 43#include <sys/bus.h> 44#include <sys/errno.h> 45#include <sys/conf.h> 46#include <sys/module.h> 47#include <sys/lock.h> 48#include <sys/mutex.h> 49#include <sys/sysctl.h> 50#include <sys/queue.h> 51#include <sys/condvar.h> 52#include <vm/uma.h> 53 54#include <sys/sockio.h> 55#include <sys/mbuf.h> 56#include <sys/socket.h> 57 58#include <net/if.h> 59#include <net/if_media.h> 60#include <net/if_atm.h> 61#include <net/route.h> 62#include <netinet/in.h> 63#include <netinet/if_atm.h> 64 65#include <machine/bus.h> 66#include <machine/resource.h> 67#include <sys/bus.h> 68#include <sys/rman.h> 69#include <sys/mbpool.h> 70 71#include <dev/utopia/utopia.h> 72#include <dev/patm/idt77252reg.h> 73#include <dev/patm/if_patmvar.h> 74 75/* 76 * Open the VCC with the given parameters 77 */ 78static int 79patm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg) 80{ 81 u_int cid; 82 struct patm_vcc *vcc; 83 int error = 0; 84 85 patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi, 86 arg->param.vci, arg->param.flags); 87 88 if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci)) 89 return (EINVAL); 90 if (arg->param.vci == 0 && (arg->param.vpi != 0 || 91 !(arg->param.flags & ATMIO_FLAG_NOTX) || 92 arg->param.aal != ATMIO_AAL_RAW)) 93 return (EINVAL); 94 cid = PATM_CID(sc, arg->param.vpi, arg->param.vci); 95 96 if ((arg->param.flags & ATMIO_FLAG_NOTX) && 97 (arg->param.flags & ATMIO_FLAG_NORX)) 98 return (EINVAL); 99 100 if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) && 101 (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX))) 102 return (EINVAL); 103 104 /* allocate vcc */ 105 vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO); 106 if (vcc == NULL) 107 return (ENOMEM); 108 109 mtx_lock(&sc->mtx); 110 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 111 /* stopped while we have analyzed the arguments */ 112 error = EIO; 113 goto done; 114 } 115 if (sc->vccs[cid] != NULL) { 116 /* ups, already open */ 117 error = EBUSY; 118 goto done; 119 } 120 121 /* check some parameters */ 122 vcc->cid = cid; 123 vcc->vcc = arg->param; 124 vcc->vflags = 0; 125 vcc->rxhand = arg->rxhand; 126 switch (vcc->vcc.aal) { 127 128 case ATMIO_AAL_0: 129 case ATMIO_AAL_34: 130 case ATMIO_AAL_5: 131 break; 132 133 case ATMIO_AAL_RAW: 134 if (arg->param.vci == 0 && 135 !(arg->param.flags & ATMIO_FLAG_NOTX)) { 136 error = EINVAL; 137 goto done; 138 } 139 break; 140 141 default: 142 error = EINVAL; 143 goto done; 144 } 145 switch (vcc->vcc.traffic) { 146 147 case ATMIO_TRAFFIC_VBR: 148 case ATMIO_TRAFFIC_UBR: 149 case ATMIO_TRAFFIC_CBR: 150 case ATMIO_TRAFFIC_ABR: 151 break; 152 153 default: 154 error = EINVAL; 155 goto done; 156 } 157 158 /* initialize */ 159 vcc->chain = NULL; 160 vcc->last = NULL; 161 vcc->ibytes = vcc->ipackets = 0; 162 vcc->obytes = vcc->opackets = 0; 163 164 /* ask the TX and RX sides */ 165 patm_debug(sc, VCC, "Open VCC: asking Rx/Tx"); 166 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) && 167 (error = patm_tx_vcc_can_open(sc, vcc)) != 0) 168 goto done; 169 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) && 170 (error = patm_rx_vcc_can_open(sc, vcc)) != 0) 171 goto done; 172 173 /* ok - go ahead */ 174 sc->vccs[cid] = vcc; 175 patm_load_vc(sc, vcc, 0); 176 177 /* don't free below */ 178 vcc = NULL; 179 sc->vccs_open++; 180 181 /* done */ 182 done: 183 mtx_unlock(&sc->mtx); 184 if (vcc != NULL) 185 uma_zfree(sc->vcc_zone, vcc); 186 return (error); 187} 188 189void 190patm_load_vc(struct patm_softc *sc, struct patm_vcc *vcc, int reload) 191{ 192 193 patm_debug(sc, VCC, "Open VCC: opening"); 194 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 195 patm_tx_vcc_open(sc, vcc); 196 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 197 patm_rx_vcc_open(sc, vcc); 198 199 if (!reload) { 200 /* inform management about non-NG and NG-PVCs */ 201 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 202 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 203 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, vcc->vcc.vpi, 204 vcc->vcc.vci, 1); 205 } 206 207 patm_debug(sc, VCC, "Open VCC: now open"); 208} 209 210/* 211 * Try to close the given VCC 212 */ 213static int 214patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg) 215{ 216 u_int cid; 217 struct patm_vcc *vcc; 218 int error = 0; 219 220 patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci); 221 222 if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci)) 223 return (EINVAL); 224 cid = PATM_CID(sc, arg->vpi, arg->vci); 225 226 mtx_lock(&sc->mtx); 227 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 228 /* stopped while we have analyzed the arguments */ 229 error = EIO; 230 goto done; 231 } 232 233 vcc = sc->vccs[cid]; 234 if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) { 235 error = ENOENT; 236 goto done; 237 } 238 239 if (vcc->vflags & PATM_VCC_TX_OPEN) 240 patm_tx_vcc_close(sc, vcc); 241 if (vcc->vflags & PATM_VCC_RX_OPEN) 242 patm_rx_vcc_close(sc, vcc); 243 244 if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) 245 goto done; 246 247 while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) { 248 cv_wait(&sc->vcc_cv, &sc->mtx); 249 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 250 /* ups, has been stopped */ 251 error = EIO; 252 goto done; 253 } 254 } 255 256 if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) 257 patm_tx_vcc_closed(sc, vcc); 258 if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) 259 patm_rx_vcc_closed(sc, vcc); 260 261 patm_vcc_closed(sc, vcc); 262 263 done: 264 mtx_unlock(&sc->mtx); 265 266 return (error); 267} 268 269/* 270 * VCC has been finally closed. 271 */ 272void 273patm_vcc_closed(struct patm_softc *sc, struct patm_vcc *vcc) 274{ 275 276 /* inform management about non-NG and NG-PVCs */ 277 if (!(vcc->vcc.flags & ATMIO_FLAG_NG) || 278 (vcc->vcc.flags & ATMIO_FLAG_PVC)) 279 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, vcc->vcc.vpi, 280 vcc->vcc.vci, 0); 281 282 sc->vccs_open--; 283 sc->vccs[vcc->cid] = NULL; 284 uma_zfree(sc->vcc_zone, vcc); 285} 286 287int 288patm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 289{ 290 struct ifreq *ifr = (struct ifreq *)data; 291 struct ifaddr *ifa = (struct ifaddr *)data; 292 struct patm_softc *sc = ifp->if_softc; 293 int error = 0; 294 uint32_t cfg; 295 struct atmio_vcctable *vtab; 296 297 switch (cmd) { 298 299 case SIOCSIFADDR: 300 mtx_lock(&sc->mtx); 301 ifp->if_flags |= IFF_UP; 302 if (!(ifp->if_flags & IFF_RUNNING)) 303 patm_initialize(sc); 304 switch (ifa->ifa_addr->sa_family) { 305 306#ifdef INET 307 case AF_INET: 308 case AF_INET6: 309 ifa->ifa_rtrequest = atm_rtrequest; 310 break; 311#endif 312 default: 313 break; 314 } 315 mtx_unlock(&sc->mtx); 316 break; 317 318 case SIOCSIFFLAGS: 319 mtx_lock(&sc->mtx); 320 if (ifp->if_flags & IFF_UP) { 321 if (!(ifp->if_flags & IFF_RUNNING)) { 322 patm_initialize(sc); 323 } 324 } else { 325 if (ifp->if_flags & IFF_RUNNING) { 326 patm_stop(sc); 327 } 328 } 329 mtx_unlock(&sc->mtx); 330 break; 331 332 case SIOCGIFMEDIA: 333 case SIOCSIFMEDIA: 334 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 335 336 /* 337 * We need to toggle unassigned/idle cells ourself because 338 * the 77252 generates null cells for spacing. When switching 339 * null cells of it gets the timing wrong. 340 */ 341 mtx_lock(&sc->mtx); 342 if (ifp->if_flags & IFF_RUNNING) { 343 if (sc->utopia.state & UTP_ST_UNASS) { 344 if (!(sc->flags & PATM_UNASS)) { 345 cfg = patm_nor_read(sc, IDT_NOR_CFG); 346 cfg &= ~IDT_CFG_IDLECLP; 347 patm_nor_write(sc, IDT_NOR_CFG, cfg); 348 sc->flags |= PATM_UNASS; 349 } 350 } else { 351 if (sc->flags & PATM_UNASS) { 352 cfg = patm_nor_read(sc, IDT_NOR_CFG); 353 cfg |= IDT_CFG_IDLECLP; 354 patm_nor_write(sc, IDT_NOR_CFG, cfg); 355 sc->flags &= ~PATM_UNASS; 356 } 357 } 358 } else { 359 if (sc->utopia.state & UTP_ST_UNASS) 360 sc->flags |= PATM_UNASS; 361 else 362 sc->flags &= ~PATM_UNASS; 363 } 364 mtx_unlock(&sc->mtx); 365 break; 366 367 case SIOCSIFMTU: 368 /* 369 * Set the interface MTU. 370 */ 371 if (ifr->ifr_mtu > ATMMTU) 372 error = EINVAL; 373 else 374 ifp->if_mtu = ifr->ifr_mtu; 375 break; 376 377 case SIOCATMOPENVCC: /* kernel internal use */ 378 error = patm_open_vcc(sc, (struct atmio_openvcc *)data); 379 break; 380 381 case SIOCATMCLOSEVCC: /* kernel internal use */ 382 error = patm_close_vcc(sc, (struct atmio_closevcc *)data); 383 break; 384 385 case SIOCATMGVCCS: /* external use */ 386 /* return vcc table */ 387 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 388 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 1); 389 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) + 390 vtab->count * sizeof(vtab->vccs[0])); 391 free(vtab, M_DEVBUF); 392 break; 393 394 case SIOCATMGETVCCS: /* netgraph internal use */ 395 vtab = atm_getvccs((struct atmio_vcc **)sc->vccs, 396 sc->mmap->max_conn, sc->vccs_open, &sc->mtx, 0); 397 if (vtab == NULL) { 398 error = ENOMEM; 399 break; 400 } 401 *(void **)data = vtab; 402 break; 403 404 default: 405 patm_debug(sc, IOCTL, "unknown cmd=%08lx arg=%p", cmd, data); 406 error = EINVAL; 407 break; 408 } 409 410 return (error); 411} 412