set_bit(ATM_VF_CLOSE, &vcc->flags);
clear_bit(ATM_VF_READY, &vcc->flags); if (vcc->dev && vcc->dev->ops->close)
vcc->dev->ops->close(vcc); if (vcc->push)
vcc->push(vcc, NULL); /* atmarpd has no push */
module_put(vcc->owner);
/* allow VCCs with same VPI/VCI iff they don't collide on TX/RX (but we may refuse such sharing for other reasons,
e.g. if protocol requires to have both channels) */
return 0;
}
staticint find_ci(conststruct atm_vcc *vcc, short *vpi, int *vci)
{ staticshort p; /* poor man's per-device cache */ staticint c; short old_p; int old_c; int err;
if (*vpi != ATM_VPI_ANY && *vci != ATM_VCI_ANY) {
err = check_ci(vcc, *vpi, *vci); return err;
} /* last scan may have left values out of bounds for current device */ if (*vpi != ATM_VPI_ANY)
p = *vpi; elseif (p >= 1 << vcc->dev->ci_range.vpi_bits)
p = 0; if (*vci != ATM_VCI_ANY)
c = *vci; elseif (c < ATM_NOT_RSV_VCI || c >= 1 << vcc->dev->ci_range.vci_bits)
c = ATM_NOT_RSV_VCI;
old_p = p;
old_c = c; do { if (!check_ci(vcc, p, c)) {
*vpi = p;
*vci = c; return 0;
} if (*vci == ATM_VCI_ANY) {
c++; if (c >= 1 << vcc->dev->ci_range.vci_bits)
c = ATM_NOT_RSV_VCI;
} if ((c == ATM_NOT_RSV_VCI || *vci != ATM_VCI_ANY) &&
*vpi == ATM_VPI_ANY) {
p++; if (p >= 1 << vcc->dev->ci_range.vpi_bits)
p = 0;
}
} while (old_p != p || old_c != c); return -EADDRINUSE;
}
staticint __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi, int vci)
{ struct sock *sk = sk_atm(vcc); int error;
if ((vpi != ATM_VPI_UNSPEC && vpi != ATM_VPI_ANY &&
vpi >> dev->ci_range.vpi_bits) || (vci != ATM_VCI_UNSPEC &&
vci != ATM_VCI_ANY && vci >> dev->ci_range.vci_bits)) return -EINVAL; if (vci > 0 && vci < ATM_NOT_RSV_VCI && !capable(CAP_NET_BIND_SERVICE)) return -EPERM;
error = -ENODEV; if (!try_module_get(dev->ops->owner)) return error;
vcc->dev = dev;
write_lock_irq(&vcc_sklist_lock); if (test_bit(ATM_DF_REMOVED, &dev->flags) ||
(error = find_ci(vcc, &vpi, &vci))) {
write_unlock_irq(&vcc_sklist_lock); goto fail_module_put;
}
vcc->vpi = vpi;
vcc->vci = vci;
__vcc_insert_socket(sk);
write_unlock_irq(&vcc_sklist_lock); switch (vcc->qos.aal) { case ATM_AAL0:
error = atm_init_aal0(vcc);
vcc->stats = &dev->stats.aal0; break; case ATM_AAL34:
error = atm_init_aal34(vcc);
vcc->stats = &dev->stats.aal34; break; case ATM_NO_AAL: /* ATM_AAL5 is also used in the "0 for default" case */
vcc->qos.aal = ATM_AAL5;
fallthrough; case ATM_AAL5:
error = atm_init_aal5(vcc);
vcc->stats = &dev->stats.aal5; break; default:
error = -EPROTOTYPE;
} if (!error)
error = adjust_tp(&vcc->qos.txtp, vcc->qos.aal); if (!error)
error = adjust_tp(&vcc->qos.rxtp, vcc->qos.aal); if (error) goto fail;
pr_debug("VCC %d.%d, AAL %d\n", vpi, vci, vcc->qos.aal);
pr_debug(" TX: %d, PCR %d..%d, SDU %d\n",
vcc->qos.txtp.traffic_class,
vcc->qos.txtp.min_pcr,
vcc->qos.txtp.max_pcr,
vcc->qos.txtp.max_sdu);
pr_debug(" RX: %d, PCR %d..%d, SDU %d\n",
vcc->qos.rxtp.traffic_class,
vcc->qos.rxtp.min_pcr,
vcc->qos.rxtp.max_pcr,
vcc->qos.rxtp.max_sdu);
if (dev->ops->open) {
error = dev->ops->open(vcc); if (error) goto fail;
} return 0;
fail:
vcc_remove_socket(sk);
fail_module_put:
module_put(dev->ops->owner); /* ensure we get dev module ref count correct */
vcc->dev = NULL; return error;
}
int vcc_connect(struct socket *sock, int itf, short vpi, int vci)
{ struct atm_dev *dev; struct atm_vcc *vcc = ATM_SD(sock); int error;
pr_debug("(vpi %d, vci %d)\n", vpi, vci); if (sock->state == SS_CONNECTED) return -EISCONN; if (sock->state != SS_UNCONNECTED) return -EINVAL; if (!(vpi || vci)) return -EINVAL;
staticint atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos)
{ int error;
/* * Don't let the QoS change the already connected AAL type nor the * traffic class.
*/ if (qos->aal != vcc->qos.aal ||
qos->rxtp.traffic_class != vcc->qos.rxtp.traffic_class ||
qos->txtp.traffic_class != vcc->qos.txtp.traffic_class) return -EINVAL;
error = adjust_tp(&qos->txtp, qos->aal); if (!error)
error = adjust_tp(&qos->rxtp, qos->aal); if (error) return error; if (!vcc->dev->ops->change_qos) return -EOPNOTSUPP; if (sk_atm(vcc)->sk_family == AF_ATMPVC) return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET); return svc_change_qos(vcc, qos);
}
staticint check_tp(conststruct atm_trafprm *tp)
{ /* @@@ Should be merged with adjust_tp */ if (!tp->traffic_class || tp->traffic_class == ATM_ANYCLASS) return 0; if (tp->traffic_class != ATM_UBR && !tp->min_pcr && !tp->pcr &&
!tp->max_pcr) return -EINVAL; if (tp->min_pcr == ATM_MAX_PCR) return -EINVAL; if (tp->min_pcr && tp->max_pcr && tp->max_pcr != ATM_MAX_PCR &&
tp->min_pcr > tp->max_pcr) return -EINVAL; /* * We allow pcr to be outside [min_pcr,max_pcr], because later * adjustment may still push it in the valid range.
*/ return 0;
}
staticint check_qos(conststruct atm_qos *qos)
{ int error;
int vcc_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ struct atm_vcc *vcc; unsignedlong value; int error;
if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) return -EINVAL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.