if_hatm_ioctl.c revision 118170
1/* 2 * Copyright (c) 2001-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 * ForeHE driver. 30 * 31 * Ioctl handler. 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/dev/hatm/if_hatm_ioctl.c 118170 2003-07-29 14:07:19Z harti $"); 36 37#include "opt_inet.h" 38#include "opt_natm.h" 39 40#include <sys/types.h> 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/malloc.h> 44#include <sys/kernel.h> 45#include <sys/bus.h> 46#include <sys/errno.h> 47#include <sys/conf.h> 48#include <sys/module.h> 49#include <sys/queue.h> 50#include <sys/syslog.h> 51#include <sys/condvar.h> 52#include <sys/sysctl.h> 53#include <vm/uma.h> 54 55#include <sys/sockio.h> 56#include <sys/mbuf.h> 57#include <sys/socket.h> 58 59#include <net/if.h> 60#include <net/if_media.h> 61#include <net/if_atm.h> 62#include <net/route.h> 63#include <netinet/in.h> 64#include <netinet/if_atm.h> 65 66#include <machine/bus.h> 67#include <machine/resource.h> 68#include <sys/bus.h> 69#include <sys/rman.h> 70#include <pci/pcireg.h> 71#include <pci/pcivar.h> 72 73#include <dev/utopia/utopia.h> 74#include <dev/hatm/if_hatmconf.h> 75#include <dev/hatm/if_hatmreg.h> 76#include <dev/hatm/if_hatmvar.h> 77 78static u_int hatm_natm_traffic = ATMIO_TRAFFIC_UBR; 79static u_int hatm_natm_pcr = 0; 80 81static int hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS); 82 83SYSCTL_DECL(_hw_atm); 84 85SYSCTL_PROC(_hw_atm, OID_AUTO, natm_traffic, CTLTYPE_UINT | CTLFLAG_RW, 86 &hatm_natm_traffic, sizeof(hatm_natm_traffic), hatm_sysctl_natm_traffic, 87 "IU", "traffic type for NATM connections"); 88SYSCTL_UINT(_hw_atm, OID_AUTO, natm_pcr, CTLFLAG_RW, 89 &hatm_natm_pcr, 0, "PCR for NATM connections"); 90 91/* 92 * Return a table of VCCs in a freshly allocated memory area. 93 * Here we have a problem: we first count, how many vccs we need 94 * to return. The we allocate the memory and finally fill it in. 95 * Because we cannot lock while calling malloc, the number of active 96 * vccs may change while we're in malloc. So we allocate a couple of 97 * vccs more and if space anyway is not enough re-iterate. 98 */ 99static struct atmio_vcctable * 100hatm_getvccs(struct hatm_softc *sc) 101{ 102 u_int cid, alloc; 103 size_t len; 104 struct atmio_vcctable *vccs; 105 struct atmio_vcc *v; 106 107 alloc = sc->open_vccs + 10; 108 vccs = NULL; 109 110 again: 111 len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]); 112 vccs = reallocf(vccs, len, M_DEVBUF, M_WAITOK); 113 bzero(vccs, len); 114 115 /* 116 * Fill in 117 */ 118 vccs->count = 0; 119 v = vccs->vccs; 120 121 mtx_lock(&sc->mtx); 122 for (cid = 0; cid < HE_MAX_VCCS; cid++) 123 if (sc->vccs[cid] != NULL && 124 (sc->vccs[cid]->vflags & (HE_VCC_RX_OPEN | 125 HE_VCC_TX_OPEN))) { 126 if (++vccs->count == alloc) { 127 /* 128 * too many - try again 129 */ 130 break; 131 } 132 *v++ = sc->vccs[cid]->param; 133 } 134 mtx_unlock(&sc->mtx); 135 136 if (cid == HE_MAX_VCCS) 137 return (vccs); 138 139 alloc *= 2; 140 goto again; 141} 142 143/* 144 * Try to open the given VCC. 145 */ 146static int 147hatm_open_vcc(struct hatm_softc *sc, struct atmio_openvcc *arg) 148{ 149 u_int cid; 150 struct hevcc *vcc; 151 int error = 0; 152 153 DBG(sc, VCC, ("Open VCC: %u.%u flags=%#x", arg->param.vpi, 154 arg->param.vci, arg->param.flags)); 155 156 if ((arg->param.vpi & ~HE_VPI_MASK) || 157 (arg->param.vci & ~HE_VCI_MASK) || 158 (arg->param.vci == 0)) 159 return (EINVAL); 160 cid = HE_CID(arg->param.vpi, arg->param.vci); 161 162 if ((arg->param.flags & ATMIO_FLAG_NOTX) && 163 (arg->param.flags & ATMIO_FLAG_NORX)) 164 return (EINVAL); 165 166 vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO); 167 if (vcc == NULL) 168 return (ENOMEM); 169 170 mtx_lock(&sc->mtx); 171 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 172 error = EIO; 173 goto done; 174 } 175 if (sc->vccs[cid] != NULL) { 176 error = EBUSY; 177 goto done; 178 } 179 vcc->param = arg->param; 180 vcc->rxhand = arg->rxhand; 181 switch (vcc->param.aal) { 182 183 case ATMIO_AAL_0: 184 case ATMIO_AAL_5: 185 case ATMIO_AAL_RAW: 186 break; 187 188 default: 189 error = EINVAL; 190 goto done; 191 } 192 switch (vcc->param.traffic) { 193 194 case ATMIO_TRAFFIC_UBR: 195 case ATMIO_TRAFFIC_CBR: 196 case ATMIO_TRAFFIC_ABR: 197 break; 198 199 default: 200 error = EINVAL; 201 goto done; 202 } 203 vcc->ntpds = 0; 204 vcc->chain = vcc->last = NULL; 205 vcc->ibytes = vcc->ipackets = 0; 206 vcc->obytes = vcc->opackets = 0; 207 208 if (!(vcc->param.flags & ATMIO_FLAG_NOTX) && 209 (error = hatm_tx_vcc_can_open(sc, cid, vcc)) != 0) 210 goto done; 211 212 /* ok - go ahead */ 213 sc->vccs[cid] = vcc; 214 215 if (!(vcc->param.flags & ATMIO_FLAG_NOTX)) 216 hatm_tx_vcc_open(sc, cid); 217 if (!(vcc->param.flags & ATMIO_FLAG_NORX)) 218 hatm_rx_vcc_open(sc, cid); 219 220 /* inform management about non-NG and NG-PVCs */ 221 if (!(vcc->param.flags & ATMIO_FLAG_NG) || 222 (vcc->param.flags & ATMIO_FLAG_PVC)) 223 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, arg->param.vpi, 224 arg->param.vci, 1); 225 226 /* don't free below */ 227 vcc = NULL; 228 229 sc->open_vccs++; 230 231 done: 232 mtx_unlock(&sc->mtx); 233 if (vcc != NULL) 234 uma_zfree(sc->vcc_zone, vcc); 235 return (error); 236} 237 238/* 239 * Enable ioctl for NATM. Map to an open ioctl. 240 */ 241static int 242hatm_open_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph) 243{ 244 struct atmio_openvcc *v; 245 int error; 246 247 if ((v = malloc(sizeof(*v), M_TEMP, M_NOWAIT | M_ZERO)) == NULL) 248 return (ENOMEM); 249 250 v->param.flags = ATM_PH_FLAGS(&ph->aph) & 251 (ATM_PH_AAL5 | ATM_PH_LLCSNAP); 252 v->param.vpi = ATM_PH_VPI(&ph->aph); 253 v->param.vci = ATM_PH_VCI(&ph->aph); 254 v->param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5) 255 ? ATMIO_AAL_5 : ATMIO_AAL_0; 256 v->param.traffic = hatm_natm_traffic; 257 v->rxhand = ph->rxhand; 258 if ((v->param.tparam.pcr = hatm_natm_pcr) == 0 || 259 hatm_natm_pcr > sc->ifatm.mib.pcr) 260 v->param.tparam.pcr = sc->ifatm.mib.pcr; 261 v->param.tparam.mcr = 0; 262 263 error = hatm_open_vcc(sc, v); 264 if (error == 0) 265 sc->vccs[HE_CID(v->param.vpi, v->param.vci)]->vflags |= 266 HE_VCC_ASYNC; 267 268 free(v, M_TEMP); 269 270 return (error); 271} 272 273/* 274 * VCC has been finally closed. 275 */ 276void 277hatm_vcc_closed(struct hatm_softc *sc, u_int cid) 278{ 279 struct hevcc *vcc = sc->vccs[cid]; 280 281 /* inform management about non-NG and NG-PVCs */ 282 if (!(vcc->param.flags & ATMIO_FLAG_NG) || 283 (vcc->param.flags & ATMIO_FLAG_PVC)) 284 ATMEV_SEND_VCC_CHANGED(&sc->ifatm, HE_VPI(cid), HE_VCI(cid), 0); 285 286 sc->open_vccs--; 287 uma_zfree(sc->vcc_zone, vcc); 288 sc->vccs[cid] = NULL; 289} 290 291/* 292 * Try to close the given VCC 293 */ 294static int 295hatm_close_vcc(struct hatm_softc *sc, struct atmio_closevcc *arg) 296{ 297 u_int cid; 298 struct hevcc *vcc; 299 int error = 0; 300 301 DBG(sc, VCC, ("Close VCC: %u.%u", arg->vpi, arg->vci)); 302 303 if((arg->vpi & ~HE_VPI_MASK) || 304 (arg->vci & ~HE_VCI_MASK) || 305 (arg->vci == 0)) 306 return (EINVAL); 307 cid = HE_CID(arg->vpi, arg->vci); 308 309 mtx_lock(&sc->mtx); 310 vcc = sc->vccs[cid]; 311 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 312 error = EIO; 313 goto done; 314 } 315 316 if (vcc == NULL || !(vcc->vflags & HE_VCC_OPEN)) { 317 error = ENOENT; 318 goto done; 319 } 320 321 if (vcc->vflags & HE_VCC_TX_OPEN) 322 hatm_tx_vcc_close(sc, cid); 323 if (vcc->vflags & HE_VCC_RX_OPEN) 324 hatm_rx_vcc_close(sc, cid); 325 326 if (vcc->vflags & HE_VCC_ASYNC) 327 goto done; 328 329 while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) && 330 (vcc->vflags & (HE_VCC_TX_CLOSING | HE_VCC_RX_CLOSING))) 331 cv_wait(&sc->vcc_cv, &sc->mtx); 332 333 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 334 error = EIO; 335 goto done; 336 } 337 338 if (!(vcc->vflags & ATMIO_FLAG_NOTX)) 339 hatm_tx_vcc_closed(sc, cid); 340 341 hatm_vcc_closed(sc, cid); 342 343 done: 344 mtx_unlock(&sc->mtx); 345 return (error); 346} 347 348static int 349hatm_close_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph) 350{ 351 struct atmio_closevcc v; 352 353 v.vpi = ATM_PH_VPI(&ph->aph); 354 v.vci = ATM_PH_VCI(&ph->aph); 355 356 return (hatm_close_vcc(sc, &v)); 357} 358 359/* 360 * IOCTL handler 361 */ 362int 363hatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 364{ 365 struct ifreq *ifr = (struct ifreq *)data; 366 struct ifaddr *ifa = (struct ifaddr *)data; 367 struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc; 368 struct atmio_vcctable *vtab; 369 int error = 0; 370 371 switch (cmd) { 372 373 case SIOCSIFADDR: 374 mtx_lock(&sc->mtx); 375 ifp->if_flags |= IFF_UP; 376 if (!(ifp->if_flags & IFF_RUNNING)) 377 hatm_initialize(sc); 378 switch (ifa->ifa_addr->sa_family) { 379 380#ifdef INET 381 case AF_INET: 382 case AF_INET6: 383 ifa->ifa_rtrequest = atm_rtrequest; 384 break; 385#endif 386 default: 387 break; 388 } 389 mtx_unlock(&sc->mtx); 390 break; 391 392 case SIOCSIFFLAGS: 393 mtx_lock(&sc->mtx); 394 if (ifp->if_flags & IFF_UP) { 395 if (!(ifp->if_flags & IFF_RUNNING)) { 396 hatm_initialize(sc); 397 } 398 } else { 399 if (ifp->if_flags & IFF_RUNNING) { 400 hatm_stop(sc); 401 } 402 } 403 mtx_unlock(&sc->mtx); 404 break; 405 406 case SIOCGIFMEDIA: 407 case SIOCSIFMEDIA: 408 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 409 break; 410 411 case SIOCSIFMTU: 412 /* 413 * Set the interface MTU. 414 */ 415 if (ifr->ifr_mtu > ATMMTU) 416 error = EINVAL; 417 else 418 ifp->if_mtu = ifr->ifr_mtu; 419 break; 420 421 case SIOCATMGVCCS: 422 /* return vcc table */ 423 vtab = hatm_getvccs(sc); 424 if (vtab == NULL) { 425 error = ENOMEM; 426 break; 427 } 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 SIOCATMENA: /* NATM internal use */ 434 error = hatm_open_vcc1(sc, (struct atm_pseudoioctl *)data); 435 break; 436 437 case SIOCATMDIS: /* NATM internal use */ 438 error = hatm_close_vcc1(sc, (struct atm_pseudoioctl *)data); 439 break; 440 441 case SIOCATMGETVCCS: /* netgraph internal use */ 442 if ((vtab = hatm_getvccs(sc)) == NULL) { 443 error = ENOMEM; 444 break; 445 } 446 *(void **)data = vtab; 447 break; 448 449 case SIOCATMOPENVCC: /* netgraph/harp internal use */ 450 error = hatm_open_vcc(sc, (struct atmio_openvcc *)data); 451 break; 452 453 case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */ 454 error = hatm_close_vcc(sc, (struct atmio_closevcc *)data); 455 break; 456 457 default: 458 DBG(sc, IOCTL, ("cmd=%08lx arg=%p", cmd, data)); 459 error = EINVAL; 460 break; 461 } 462 463 return (error); 464} 465 466static int 467hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS) 468{ 469 int error; 470 int tmp; 471 472 tmp = hatm_natm_traffic; 473 error = sysctl_handle_int(oidp, &tmp, 0, req); 474 if (error != 0 || req->newptr == NULL) 475 return (error); 476 477 if (tmp != ATMIO_TRAFFIC_UBR && tmp != ATMIO_TRAFFIC_CBR) 478 return (EINVAL); 479 480 hatm_natm_traffic = tmp; 481 return (0); 482} 483