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