/* * Try to close the given VCC */ static int patm_close_vcc(struct patm_softc *sc, struct atmio_closevcc *arg) { u_int cid; struct patm_vcc *vcc; int error = 0; patm_debug(sc, VCC, "Close VCC: %u.%u", arg->vpi, arg->vci); if (!LEGAL_VPI(sc, arg->vpi) || !LEGAL_VCI(sc, arg->vci)) return (EINVAL); cid = PATM_CID(sc, arg->vpi, arg->vci); mtx_lock(&sc->mtx); if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* stopped while we have analyzed the arguments */ error = EIO; goto done; } vcc = sc->vccs[cid]; if (vcc == NULL || !(vcc->vflags & PATM_VCC_OPEN)) { error = ENOENT; goto done; } if (vcc->vflags & PATM_VCC_TX_OPEN) patm_tx_vcc_close(sc, vcc); if (vcc->vflags & PATM_VCC_RX_OPEN) patm_rx_vcc_close(sc, vcc); if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) goto done; while (vcc->vflags & (PATM_VCC_TX_CLOSING | PATM_VCC_RX_CLOSING)) { cv_wait(&sc->vcc_cv, &sc->mtx); if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* ups, has been stopped */ error = EIO; goto done; } } if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX)) patm_tx_vcc_closed(sc, vcc); if (!(vcc->vcc.flags & ATMIO_FLAG_NORX)) patm_rx_vcc_closed(sc, vcc); patm_vcc_closed(sc, vcc); done: mtx_unlock(&sc->mtx); return (error); }
/* * Handle the given receive status queue entry */ void patm_rx(struct patm_softc *sc, struct idt_rsqe *rsqe) { struct mbuf *m; void *buf; u_int stat, cid, w, cells, len, h; struct patm_vcc *vcc; struct atm_pseudohdr aph; u_char *trail; cid = le32toh(rsqe->cid); stat = le32toh(rsqe->stat); h = le32toh(rsqe->handle); cid = PATM_CID(sc, IDT_RSQE_VPI(cid), IDT_RSQE_VCI(cid)); vcc = sc->vccs[cid]; if (IDT_RSQE_TYPE(stat) == IDT_RSQE_IDLE) { /* connection has gone idle */ if (stat & IDT_RSQE_BUF) patm_rcv_free(sc, patm_rcv_handle(sc, h), h); w = rct_read(sc, cid, 0); if (w != 0 && !(w & IDT_RCT_OPEN)) rct_write(sc, cid, 0, 0); if (vcc != NULL && (vcc->vflags & PATM_VCC_RX_CLOSING)) { patm_debug(sc, VCC, "%u.%u RX closed", vcc->vcc.vpi, vcc->vcc.vci); vcc->vflags &= ~PATM_VCC_RX_CLOSING; if (vcc->vcc.flags & ATMIO_FLAG_ASYNC) { patm_rx_vcc_closed(sc, vcc); if (!(vcc->vflags & PATM_VCC_OPEN)) patm_vcc_closed(sc, vcc); } else cv_signal(&sc->vcc_cv); } return; } buf = patm_rcv_handle(sc, h); if (vcc == NULL || (vcc->vflags & PATM_VCC_RX_OPEN) == 0) { patm_rcv_free(sc, buf, h); return; } cells = IDT_RSQE_CNT(stat); KASSERT(cells > 0, ("zero cell count")); if (vcc->vcc.aal == ATMIO_AAL_0) { /* deliver this packet as it is */ if ((m = patm_rcv_mbuf(sc, buf, h, 1)) == NULL) return; m->m_len = cells * 48; m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = sc->ifp; } else if (vcc->vcc.aal == ATMIO_AAL_34) { /* XXX AAL3/4 */ patm_rcv_free(sc, buf, h); return; } else if (vcc->vcc.aal == ATMIO_AAL_5) { if (stat & IDT_RSQE_CRC) { if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); if (vcc->chain != NULL) { m_freem(vcc->chain); vcc->chain = vcc->last = NULL; } return; } /* append to current chain */ if (vcc->chain == NULL) { if ((m = patm_rcv_mbuf(sc, buf, h, 1)) == NULL) return; m->m_len = cells * 48; m->m_pkthdr.len = m->m_len; m->m_pkthdr.rcvif = sc->ifp; vcc->chain = vcc->last = m; } else { if ((m = patm_rcv_mbuf(sc, buf, h, 0)) == NULL) return; m->m_len = cells * 48; vcc->last->m_next = m; vcc->last = m; vcc->chain->m_pkthdr.len += m->m_len; } if (!(stat & IDT_RSQE_EPDU)) return; trail = mtod(m, u_char *) + m->m_len - 6; len = (trail[0] << 8) + trail[1]; if ((u_int)vcc->chain->m_pkthdr.len < len + 8) { patm_printf(sc, "%s: bad aal5 lengths %u %u\n", __func__, (u_int)m->m_pkthdr.len, len); m_freem(vcc->chain); vcc->chain = vcc->last = NULL; return; } m->m_len -= vcc->chain->m_pkthdr.len - len; KASSERT(m->m_len >= 0, ("bad last mbuf")); m = vcc->chain; vcc->chain = vcc->last = NULL; m->m_pkthdr.len = len; } else
/* * Open the VCC with the given parameters */ static int patm_open_vcc(struct patm_softc *sc, struct atmio_openvcc *arg) { u_int cid; struct patm_vcc *vcc; int error = 0; patm_debug(sc, VCC, "Open VCC: %u.%u flags=%#x", arg->param.vpi, arg->param.vci, arg->param.flags); if (!LEGAL_VPI(sc, arg->param.vpi) || !LEGAL_VCI(sc, arg->param.vci)) return (EINVAL); if (arg->param.vci == 0 && (arg->param.vpi != 0 || !(arg->param.flags & ATMIO_FLAG_NOTX) || arg->param.aal != ATMIO_AAL_RAW)) return (EINVAL); cid = PATM_CID(sc, arg->param.vpi, arg->param.vci); if ((arg->param.flags & ATMIO_FLAG_NOTX) && (arg->param.flags & ATMIO_FLAG_NORX)) return (EINVAL); if ((arg->param.traffic == ATMIO_TRAFFIC_ABR) && (arg->param.flags & (ATMIO_FLAG_NOTX | ATMIO_FLAG_NORX))) return (EINVAL); /* allocate vcc */ vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO); if (vcc == NULL) return (ENOMEM); mtx_lock(&sc->mtx); if (!(sc->ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* stopped while we have analyzed the arguments */ error = EIO; goto done; } if (sc->vccs[cid] != NULL) { /* ups, already open */ error = EBUSY; goto done; } /* check some parameters */ vcc->cid = cid; vcc->vcc = arg->param; vcc->vflags = 0; vcc->rxhand = arg->rxhand; switch (vcc->vcc.aal) { case ATMIO_AAL_0: case ATMIO_AAL_34: case ATMIO_AAL_5: break; case ATMIO_AAL_RAW: if (arg->param.vci == 0 && !(arg->param.flags & ATMIO_FLAG_NOTX)) { error = EINVAL; goto done; } break; default: error = EINVAL; goto done; } switch (vcc->vcc.traffic) { case ATMIO_TRAFFIC_VBR: case ATMIO_TRAFFIC_UBR: case ATMIO_TRAFFIC_CBR: case ATMIO_TRAFFIC_ABR: break; default: error = EINVAL; goto done; } /* initialize */ vcc->chain = NULL; vcc->last = NULL; vcc->ibytes = vcc->ipackets = 0; vcc->obytes = vcc->opackets = 0; /* ask the TX and RX sides */ patm_debug(sc, VCC, "Open VCC: asking Rx/Tx"); if (!(vcc->vcc.flags & ATMIO_FLAG_NOTX) && (error = patm_tx_vcc_can_open(sc, vcc)) != 0) goto done; if (!(vcc->vcc.flags & ATMIO_FLAG_NORX) && (error = patm_rx_vcc_can_open(sc, vcc)) != 0) goto done; /* ok - go ahead */ sc->vccs[cid] = vcc; patm_load_vc(sc, vcc, 0); /* don't free below */ vcc = NULL; sc->vccs_open++; /* done */ done: mtx_unlock(&sc->mtx); if (vcc != NULL) uma_zfree(sc->vcc_zone, vcc); return (error); }