if_hatm_ioctl.c revision 116519
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 116519 2003-06-18 09:31:37Z 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#ifdef notyet 221 /* inform management about non-NG and NG-PVCs */ 222 if (!(vcc->param.flags & ATMIO_FLAG_NG) || 223 (vcc->param.flags & ATMIO_FLAG_PVC)) 224 atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED, 225 (1 << 24) | (arg->vpi << 16) | arg->vci); 226#endif 227 228 /* don't free below */ 229 vcc = NULL; 230 231 sc->open_vccs++; 232 233 done: 234 mtx_unlock(&sc->mtx); 235 if (vcc != NULL) 236 uma_zfree(sc->vcc_zone, vcc); 237 return (error); 238} 239 240/* 241 * Enable ioctl for NATM. Map to an open ioctl. 242 */ 243static int 244hatm_open_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph) 245{ 246 struct atmio_openvcc *v; 247 int error; 248 249 if ((v = malloc(sizeof(*v), M_TEMP, M_NOWAIT | M_ZERO)) == NULL) 250 return (ENOMEM); 251 252 v->param.flags = ATM_PH_FLAGS(&ph->aph) & 253 (ATM_PH_AAL5 | ATM_PH_LLCSNAP); 254 v->param.vpi = ATM_PH_VPI(&ph->aph); 255 v->param.vci = ATM_PH_VCI(&ph->aph); 256 v->param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5) 257 ? ATMIO_AAL_5 : ATMIO_AAL_0; 258 v->param.traffic = hatm_natm_traffic; 259 v->rxhand = ph->rxhand; 260 if ((v->param.tparam.pcr = hatm_natm_pcr) == 0 || 261 hatm_natm_pcr > sc->ifatm.mib.pcr) 262 v->param.tparam.pcr = sc->ifatm.mib.pcr; 263 v->param.tparam.mcr = 0; 264 265 error = hatm_open_vcc(sc, v); 266 if (error == 0) 267 sc->vccs[HE_CID(v->param.vpi, v->param.vci)]->vflags |= 268 HE_VCC_ASYNC; 269 270 free(v, M_TEMP); 271 272 return (error); 273} 274 275/* 276 * VCC has been finally closed. 277 */ 278void 279hatm_vcc_closed(struct hatm_softc *sc, u_int cid) 280{ 281 struct hevcc *vcc = sc->vccs[cid]; 282 283#ifdef notyet 284 /* inform management about non-NG and NG-PVCs */ 285 if (!(vcc->param.flags & ATMIO_FLAG_NG) || 286 (vcc->param.flags & ATMIO_FLAG_PVC)) 287 atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED, 288 (0 << 24) | (HE_VPI(cid) << 16) | HE_VCI(cid)); 289#endif 290 291 sc->open_vccs--; 292 uma_zfree(sc->vcc_zone, vcc); 293 sc->vccs[cid] = NULL; 294} 295 296/* 297 * Try to close the given VCC 298 */ 299static int 300hatm_close_vcc(struct hatm_softc *sc, struct atmio_closevcc *arg) 301{ 302 u_int cid; 303 struct hevcc *vcc; 304 int error = 0; 305 306 DBG(sc, VCC, ("Close VCC: %u.%u", arg->vpi, arg->vci)); 307 308 if((arg->vpi & ~HE_VPI_MASK) || 309 (arg->vci & ~HE_VCI_MASK) || 310 (arg->vci == 0)) 311 return (EINVAL); 312 cid = HE_CID(arg->vpi, arg->vci); 313 314 mtx_lock(&sc->mtx); 315 vcc = sc->vccs[cid]; 316 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 317 error = EIO; 318 goto done; 319 } 320 321 if (vcc == NULL || !(vcc->vflags & HE_VCC_OPEN)) { 322 error = ENOENT; 323 goto done; 324 } 325 326 if (vcc->vflags & HE_VCC_TX_OPEN) 327 hatm_tx_vcc_close(sc, cid); 328 if (vcc->vflags & HE_VCC_RX_OPEN) 329 hatm_rx_vcc_close(sc, cid); 330 331 if (vcc->vflags & HE_VCC_ASYNC) 332 goto done; 333 334 while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) && 335 (vcc->vflags & (HE_VCC_TX_CLOSING | HE_VCC_RX_CLOSING))) 336 cv_wait(&sc->vcc_cv, &sc->mtx); 337 338 if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) { 339 error = EIO; 340 goto done; 341 } 342 343 if (!(vcc->vflags & ATMIO_FLAG_NOTX)) 344 hatm_tx_vcc_closed(sc, cid); 345 346 hatm_vcc_closed(sc, cid); 347 348 done: 349 mtx_unlock(&sc->mtx); 350 return (error); 351} 352 353static int 354hatm_close_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph) 355{ 356 struct atmio_closevcc v; 357 358 v.vpi = ATM_PH_VPI(&ph->aph); 359 v.vci = ATM_PH_VCI(&ph->aph); 360 361 return (hatm_close_vcc(sc, &v)); 362} 363 364/* 365 * IOCTL handler 366 */ 367int 368hatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 369{ 370 struct ifreq *ifr = (struct ifreq *)data; 371 struct ifaddr *ifa = (struct ifaddr *)data; 372 struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc; 373 struct atmio_vcctable *vtab; 374 int error = 0; 375 376 switch (cmd) { 377 378 case SIOCSIFADDR: 379 mtx_lock(&sc->mtx); 380 ifp->if_flags |= IFF_UP; 381 if (!(ifp->if_flags & IFF_RUNNING)) 382 hatm_initialize(sc); 383 switch (ifa->ifa_addr->sa_family) { 384 385#ifdef INET 386 case AF_INET: 387 case AF_INET6: 388 ifa->ifa_rtrequest = atm_rtrequest; 389 break; 390#endif 391 default: 392 break; 393 } 394 mtx_unlock(&sc->mtx); 395 break; 396 397 case SIOCSIFFLAGS: 398 mtx_lock(&sc->mtx); 399 if (ifp->if_flags & IFF_UP) { 400 if (!(ifp->if_flags & IFF_RUNNING)) { 401 hatm_initialize(sc); 402 } 403 } else { 404 if (ifp->if_flags & IFF_RUNNING) { 405 hatm_stop(sc); 406 } 407 } 408 mtx_unlock(&sc->mtx); 409 break; 410 411 case SIOCGIFMEDIA: 412 case SIOCSIFMEDIA: 413 error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 414 break; 415 416 case SIOCSIFMTU: 417 /* 418 * Set the interface MTU. 419 */ 420 if (ifr->ifr_mtu > ATMMTU) 421 error = EINVAL; 422 else 423 ifp->if_mtu = ifr->ifr_mtu; 424 break; 425 426 case SIOCATMGVCCS: 427 /* return vcc table */ 428 vtab = hatm_getvccs(sc); 429 if (vtab == NULL) { 430 error = ENOMEM; 431 break; 432 } 433 error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) + 434 vtab->count * sizeof(vtab->vccs[0])); 435 free(vtab, M_DEVBUF); 436 break; 437 438 case SIOCATMENA: /* NATM internal use */ 439 error = hatm_open_vcc1(sc, (struct atm_pseudoioctl *)data); 440 break; 441 442 case SIOCATMDIS: /* NATM internal use */ 443 error = hatm_close_vcc1(sc, (struct atm_pseudoioctl *)data); 444 break; 445 446 case SIOCATMGETVCCS: /* netgraph internal use */ 447 if ((vtab = hatm_getvccs(sc)) == NULL) { 448 error = ENOMEM; 449 break; 450 } 451 *(void **)data = vtab; 452 break; 453 454 case SIOCATMOPENVCC: /* netgraph/harp internal use */ 455 error = hatm_open_vcc(sc, (struct atmio_openvcc *)data); 456 break; 457 458 case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */ 459 error = hatm_close_vcc(sc, (struct atmio_closevcc *)data); 460 break; 461 462 default: 463 DBG(sc, IOCTL, ("cmd=%08lx arg=%p", cmd, data)); 464 error = EINVAL; 465 break; 466 } 467 468 return (error); 469} 470 471static int 472hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS) 473{ 474 int error; 475 int tmp; 476 477 tmp = hatm_natm_traffic; 478 error = sysctl_handle_int(oidp, &tmp, 0, req); 479 if (error != 0 || req->newptr == NULL) 480 return (error); 481 482 if (tmp != ATMIO_TRAFFIC_UBR && tmp != ATMIO_TRAFFIC_CBR) 483 return (EINVAL); 484 485 hatm_natm_traffic = tmp; 486 return (0); 487} 488