/* * * =================================== * HARP | Host ATM Research Platform * =================================== * * * This Host ATM Research Platform ("HARP") file (the "Software") is * made available by Network Computing Services, Inc. ("NetworkCS") * "AS IS". NetworkCS does not provide maintenance, improvements or * support of any kind. * * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED, * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE. * In no event shall NetworkCS be responsible for any damages, including * but not limited to consequential damages, arising from or relating to * any use of the Software or related support. * * Copyright 1994-1998 Network Computing Services, Inc. * * Copies of this Software may be made, however, the above copyright * notice must be reproduced on all copies. * * @(#) $FreeBSD$ * */ /* * Core ATM Services * ----------------- * * ATM Connection Manager * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif /* * Global variables */ struct atm_cm_stat atm_cm_stat = {0}; /* * Local functions */ static void atm_cm_cpcs_upper(int, void *, int, int); static void atm_cm_saal_upper(int, void *, int, int); static void atm_cm_sscop_upper(int, void *, int, int); static Atm_connvc * atm_cm_share_llc(Atm_attributes *); static void atm_cm_closeconn(Atm_connection *, struct t_atm_cause *); static void atm_cm_closevc(Atm_connvc *); static void atm_cm_timeout(struct atm_time *); static KTimeout_ret atm_cm_procinq(void *); static void atm_cm_incall(Atm_connvc *); static int atm_cm_accept(Atm_connvc *, Atm_connection *); /* * Local variables */ static Queue_t atm_connection_queue = {NULL}; static Queue_t atm_incoming_queue = {NULL}; static int atm_incoming_qlen = 0; static Atm_connection *atm_listen_queue = NULL; static struct attr_cause atm_cause_tmpl = {T_ATM_PRESENT, {T_ATM_ITU_CODING, T_ATM_LOC_USER, 0, {0, 0, 0, 0}}}; /* * Stack commands, indexed by API */ static struct { int init; int term; } atm_stackcmds[] = { {CPCS_INIT, CPCS_TERM}, /* CMAPI_CPCS */ {SSCF_UNI_INIT, SSCF_UNI_TERM}, /* CMAPI_SAAL */ {SSCOP_INIT, SSCOP_TERM}, /* CMAPI_SSCOP */ }; static struct sp_info atm_connection_pool = { "atm connection pool", /* si_name */ sizeof(Atm_connection), /* si_blksiz */ 10, /* si_blkcnt */ 100 /* si_maxallow */ }; static struct sp_info atm_connvc_pool = { "atm connection vcc pool", /* si_name */ sizeof(Atm_connvc), /* si_blksiz */ 10, /* si_blkcnt */ 100 /* si_maxallow */ }; /* * Initiate Outgoing ATM Call * * Called by an endpoint service to create a new Connection Manager API * instance and to initiate an outbound ATM connection. The endpoint * provided token will be used in all further CM -> endpoint function * calls, and the returned connection block pointer must be used in all * subsequent endpoint -> CM function calls. * * If the return indicates that the connection setup has been immediately * successful (typically only for PVCs and shared SVCs), then the connection * is ready for data transmission. * * If the return indicates that the connection setup is still in progress, * then the endpoint must wait for notification from the Connection Manager * indicating the final status of the call setup. If the call setup completes * successfully, then a "call connected" notification will be sent to the * endpoint by the Connection Manager. If the call setup fails, then the * endpoint will receive a "call cleared" notification. * * All connection instances must be freed with an atm_cm_release() call. * * Arguments: * epp pointer to endpoint definition structure * token endpoint's connection instance token * ap pointer to requested connection attributes * copp pointer to location to return allocated connection block * * Returns: * 0 connection has been successfully established * EINPROGRESS connection establishment is in progress * errno connection failed - reason indicated * */ int atm_cm_connect(epp, token, ap, copp) Atm_endpoint *epp; void *token; Atm_attributes *ap; Atm_connection **copp; { Atm_connection *cop; Atm_connvc *cvp; struct atm_pif *pip; struct sigmgr *smp; struct stack_list sl; void (*upf)(int, void *, int, int); int s, sli, err, err2; *copp = NULL; cvp = NULL; /* * Get a connection block */ cop = (Atm_connection *)atm_allocate(&atm_connection_pool); if (cop == NULL) return (ENOMEM); /* * Initialize connection info */ cop->co_endpt = epp; cop->co_toku = token; /* * Initialize stack list index */ sli = 0; /* * Validate and extract useful attribute information */ /* * Must specify a network interface (validated below) */ if (ap->nif == NULL) { err = EINVAL; goto done; } /* * Check out Data API */ switch (ap->api) { case CMAPI_CPCS: upf = atm_cm_cpcs_upper; break; case CMAPI_SAAL: sl.sl_sap[sli++] = SAP_SSCF_UNI; sl.sl_sap[sli++] = SAP_SSCOP; upf = atm_cm_saal_upper; break; case CMAPI_SSCOP: sl.sl_sap[sli++] = SAP_SSCOP; upf = atm_cm_sscop_upper; break; default: err = EINVAL; goto done; } /* * AAL Attributes */ if (ap->aal.tag != T_ATM_PRESENT) { err = EINVAL; goto done; } switch (ap->aal.type) { case ATM_AAL5: sl.sl_sap[sli++] = SAP_CPCS_AAL5; sl.sl_sap[sli++] = SAP_SAR_AAL5; sl.sl_sap[sli++] = SAP_ATM; break; case ATM_AAL3_4: sl.sl_sap[sli++] = SAP_CPCS_AAL3_4; sl.sl_sap[sli++] = SAP_SAR_AAL3_4; sl.sl_sap[sli++] = SAP_ATM; break; default: err = EINVAL; goto done; } /* * Broadband Bearer Attributes */ if (ap->bearer.tag != T_ATM_PRESENT) { err = EINVAL; goto done; } switch (ap->bearer.v.connection_configuration) { case T_ATM_1_TO_1: cop->co_flags |= COF_P2P; break; case T_ATM_1_TO_MANY: /* Not supported */ cop->co_flags |= COF_P2MP; err = EINVAL; goto done; default: err = EINVAL; goto done; } /* * Logical Link Control Attributes */ if (ap->llc.tag == T_ATM_PRESENT) { if ((ap->blli.tag_l2 != T_ATM_PRESENT) || (ap->blli.v.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) || (ap->blli.v.layer_2_protocol.ID.simple_ID != T_ATM_BLLI2_I8802) || (ap->llc.v.llc_len < T_ATM_LLC_MIN_LEN) || (ap->llc.v.llc_len > T_ATM_LLC_MAX_LEN)) { err = EINVAL; goto done; } cop->co_mpx = ATM_ENC_LLC; cop->co_llc = ap->llc; } else cop->co_mpx = ATM_ENC_NULL; /* * Called Party Attributes */ if (ap->called.tag != T_ATM_PRESENT) { err = EINVAL; goto done; } if ((ap->called.addr.address_format == T_ATM_ABSENT) || (ap->called.addr.address_length == 0)) { err = EINVAL; goto done; } /* * Calling Party Attributes */ if (ap->calling.tag != T_ATM_ABSENT) { err = EINVAL; goto done; } /* * Quality of Service Attributes */ if (ap->qos.tag != T_ATM_PRESENT) { err = EINVAL; goto done; } /* * Terminate stack list */ sl.sl_sap[sli] = 0; s = splnet(); /* * Let multiplexors decide whether we need a new VCC */ switch (cop->co_mpx) { case ATM_ENC_NULL: /* * All of these connections require a new VCC */ break; case ATM_ENC_LLC: /* * See if we can share an existing LLC connection */ cvp = atm_cm_share_llc(ap); if (cvp == NULL) break; /* * We've got a connection to share */ cop->co_connvc = cvp; if (cvp->cvc_state == CVCS_ACTIVE) { cop->co_state = COS_ACTIVE; err = 0; } else { cop->co_state = COS_OUTCONN; err = EINPROGRESS; } LINK2TAIL(cop, Atm_connection, cvp->cvc_conn->co_mxh, co_next); cop->co_mxh = cvp->cvc_conn->co_mxh; *copp = cop; (void) splx(s); return (err); default: panic("atm_cm_connect: unknown mpx"); } /* * If we get here, it means we need to create * a new VCC for this connection */ /* * Validate that network interface is registered and that * a signalling manager is attached */ for (pip = atm_interface_head; pip != NULL; pip = pip->pif_next) { struct atm_nif *nip; for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) { if (nip == ap->nif) break; } if (nip) break; } if (pip == NULL) { err = ENXIO; goto donex; } if ((smp = pip->pif_sigmgr) == NULL) { err = ENXIO; goto donex; } /* * Get a connection VCC block */ cvp = (Atm_connvc *)atm_allocate(&atm_connvc_pool); if (cvp == NULL) { err = ENOMEM; goto donex; } /* * Save VCC attributes */ cvp->cvc_attr = *ap; cvp->cvc_flags |= CVCF_CALLER; /* * Link the control blocks */ cop->co_connvc = cvp; cvp->cvc_conn = cop; cvp->cvc_sigmgr = smp; /* * Create a service stack */ err = atm_create_stack(cvp, &sl, upf); if (err) { cvp->cvc_state = CVCS_CLEAR; atm_cm_closevc(cvp); goto donex; } /* * Let the signalling manager handle the VCC creation */ cvp->cvc_state = CVCS_SETUP; switch ((*smp->sm_setup)(cvp, &err)) { case CALL_CONNECTED: /* * Connection is fully setup - initialize the stack */ cvp->cvc_state = CVCS_INIT; STACK_CALL(atm_stackcmds[ap->api].init, cvp->cvc_lower, cvp->cvc_tokl, cvp, ap->api_init, 0, err2); if (err2) panic("atm_cm_connect: init"); if (cvp->cvc_flags & CVCF_ABORTING) { /* * Someone on the stack bailed out...schedule the * VCC and stack termination */ atm_cm_closevc(cvp); err = EFAULT; } else { /* * Everything looks fine from here */ cvp->cvc_state = CVCS_ACTIVE; cop->co_state = COS_ACTIVE; } break; case CALL_FAILED: /* * Terminate stack and clean up before we leave */ cvp->cvc_state = CVCS_CLEAR; atm_cm_closevc(cvp); break; case CALL_PROCEEDING: /* * We'll just wait for final call status */ cop->co_state = COS_OUTCONN; err = EINPROGRESS; break; default: panic("atm_cm_connect: setup"); } donex: (void) splx(s); done: if (err && err != EINPROGRESS) { /* * Undo any partial setup stuff */ if (cop) atm_free((caddr_t)cop); } else { /* * Finish connection setup */ s = splnet(); cvp->cvc_flags |= CVCF_CONNQ; ENQUEUE(cvp, Atm_connvc, cvc_q, atm_connection_queue); LINK2TAIL(cop, Atm_connection, cop->co_mxh, co_next); (void) splx(s); *copp = cop; } return (err); } /* * Listen for Incoming ATM Calls * * Called by an endpoint service in order to indicate its willingness to * accept certain incoming calls. The types of calls which the endpoint * is prepared to accept are specified in the Atm_attributes parameter. * * For each call which meets the criteria specified by the endpoint, the * endpoint service will receive an incoming call notification via the * endpoint's ep_incoming() function. * * To cancel the listening connection, the endpoint user should invoke * atm_cm_release(). * * Arguments: * epp pointer to endpoint definition structure * token endpoint's listen instance token * ap pointer to listening connection attributes * copp pointer to location to return allocated connection block * * Returns: * 0 listening connection installed * errno listen failed - reason indicated * */ int atm_cm_listen(epp, token, ap, copp) Atm_endpoint *epp; void *token; Atm_attributes *ap; Atm_connection **copp; { Atm_connection *cop; int s, err = 0; *copp = NULL; /* * Get a connection block */ cop = (Atm_connection *)atm_allocate(&atm_connection_pool); if (cop == NULL) return (ENOMEM); /* * Initialize connection info */ cop->co_endpt = epp; cop->co_toku = token; cop->co_mxh = cop; /* * Validate and extract useful attribute information */ /* * Check out Data API */ switch (ap->api) { case CMAPI_CPCS: case CMAPI_SAAL: case CMAPI_SSCOP: break; default: err = EINVAL; goto done; } /* * AAL Attributes */ switch (ap->aal.tag) { case T_ATM_PRESENT: switch (ap->aal.type) { case ATM_AAL5: case ATM_AAL3_4: break; default: err = EINVAL; goto done; } break; case T_ATM_ABSENT: case T_ATM_ANY: break; default: err = EINVAL; goto done; } /* * Broadband High Layer Information Attributes */ switch (ap->bhli.tag) { case T_ATM_PRESENT: case T_ATM_ABSENT: case T_ATM_ANY: break; default: err = EINVAL; goto done; } /* * Broadband Low Layer Information Attributes */ switch (ap->blli.tag_l2) { case T_ATM_PRESENT: case T_ATM_ABSENT: case T_ATM_ANY: break; default: err = EINVAL; goto done; } switch (ap->blli.tag_l3) { case T_ATM_PRESENT: case T_ATM_ABSENT: case T_ATM_ANY: break; default: err = EINVAL; goto done; } /* * Logical Link Control Attributes */ switch (ap->llc.tag) { case T_ATM_PRESENT: if ((ap->blli.tag_l2 != T_ATM_PRESENT) || (ap->blli.v.layer_2_protocol.ID_type != T_ATM_SIMPLE_ID) || (ap->blli.v.layer_2_protocol.ID.simple_ID != T_ATM_BLLI2_I8802) || (ap->llc.v.llc_len < T_ATM_LLC_MIN_LEN) || (ap->llc.v.llc_len > T_ATM_LLC_MAX_LEN)) { err = EINVAL; goto done; } cop->co_mpx = ATM_ENC_LLC; cop->co_llc = ap->llc; break; case T_ATM_ABSENT: case T_ATM_ANY: cop->co_mpx = ATM_ENC_NULL; break; default: err = EINVAL; goto done; } /* * Called Party Attributes */ switch (ap->called.tag) { case T_ATM_PRESENT: switch (ap->called.addr.address_format) { case T_ATM_ABSENT: ap->called.tag = T_ATM_ABSENT; break; case T_ATM_PVC_ADDR: err = EINVAL; goto done; } break; case T_ATM_ABSENT: case T_ATM_ANY: break; default: err = EINVAL; goto done; } /* * Get an attribute block and save listening attributes */ cop->co_lattr = uma_zalloc(atm_attributes_zone, M_ZERO); if (cop->co_lattr == NULL) { err = ENOMEM; goto done; } *cop->co_lattr = *ap; /* * Now try to register the listening connection */ s = splnet(); if (atm_cm_match(cop->co_lattr, NULL) != NULL) { /* * Can't have matching listeners */ err = EADDRINUSE; goto donex; } cop->co_state = COS_LISTEN; LINK2TAIL(cop, Atm_connection, atm_listen_queue, co_next); donex: (void) splx(s); done: if (err) { /* * Undo any partial setup stuff */ if (cop) { if (cop->co_lattr) uma_zfree(atm_attributes_zone, cop->co_lattr); atm_free((caddr_t)cop); } } else { /* * Finish connection setup */ *copp = cop; } return (err); } /* * Add to LLC Connection * * Called by an endpoint service to create a new Connection Manager API * instance to be associated with an LLC-multiplexed connection instance * which has been previously created. The endpoint provided token will * be used in all further CM -> endpoint function calls, and the returned * connection block pointer must be used in all subsequent endpoint -> CM * function calls. * * If the return indicates that the connection setup has been immediately * successful, then the connection is ready for data transmission. * * If the return indicates that the connection setup is still in progress, * then the endpoint must wait for notification from the Connection Manager * indicating the final status of the call setup. If the call setup completes * successfully, then a "call connected" notification will be sent to the * endpoint by the Connection Manager. If the call setup fails, then the * endpoint will receive a "call cleared" notification. * * All connection instances must be freed with an atm_cm_release() call. * * Arguments: * epp pointer to endpoint definition structure * token endpoint's connection instance token * llc pointer to llc attributes for new connection * ecop pointer to existing connection block * copp pointer to location to return allocated connection block * * Returns: * 0 connection has been successfully established * EINPROGRESS connection establishment is in progress * errno addllc failed - reason indicated * */ int atm_cm_addllc(epp, token, llc, ecop, copp) Atm_endpoint *epp; void *token; struct attr_llc *llc; Atm_connection *ecop; Atm_connection **copp; { Atm_connection *cop, *cop2; Atm_connvc *cvp; int s, err; *copp = NULL; /* * Check out requested LLC attributes */ if ((llc->tag != T_ATM_PRESENT) || ((llc->v.flags & T_ATM_LLC_SHARING) == 0) || (llc->v.llc_len < T_ATM_LLC_MIN_LEN) || (llc->v.llc_len > T_ATM_LLC_MAX_LEN)) return (EINVAL); /* * Get a connection block */ cop = (Atm_connection *)atm_allocate(&atm_connection_pool); if (cop == NULL) return (ENOMEM); /* * Initialize connection info */ cop->co_endpt = epp; cop->co_toku = token; cop->co_llc = *llc; s = splnet(); /* * Ensure that supplied connection is really valid */ cop2 = NULL; for (cvp = Q_HEAD(atm_connection_queue, Atm_connvc); cvp; cvp = Q_NEXT(cvp, Atm_connvc, cvc_q)) { for (cop2 = cvp->cvc_conn; cop2; cop2 = cop2->co_next) { if (ecop == cop2) break; } if (cop2) break; } if (cop2 == NULL) { err = ENOENT; goto done; } switch (ecop->co_state) { case COS_OUTCONN: case COS_INACCEPT: err = EINPROGRESS; break; case COS_ACTIVE: err = 0; break; default: err = EINVAL; goto done; } /* * Connection must be LLC multiplexed and shared */ if ((ecop->co_mpx != ATM_ENC_LLC) || ((ecop->co_llc.v.flags & T_ATM_LLC_SHARING) == 0)) { err = EINVAL; goto done; } /* * This new LLC header must be unique for this VCC */ cop2 = ecop->co_mxh; while (cop2) { int i = MIN(llc->v.llc_len, cop2->co_llc.v.llc_len); if (bcmp(llc->v.llc_info, cop2->co_llc.v.llc_info, i) == 0) { err = EINVAL; goto done; } cop2 = cop2->co_next; } /* * Everything seems to check out */ cop->co_flags = ecop->co_flags; cop->co_state = ecop->co_state; cop->co_mpx = ecop->co_mpx; cop->co_connvc = ecop->co_connvc; LINK2TAIL(cop, Atm_connection, ecop->co_mxh, co_next); cop->co_mxh = ecop->co_mxh; done: (void) splx(s); if (err && err != EINPROGRESS) { /* * Undo any partial setup stuff */ if (cop) atm_free((caddr_t)cop); } else { /* * Pass new connection back to caller */ *copp = cop; } return (err); } /* * XXX * * Arguments: * cop pointer to connection block * id identifier for party to be added * addr address of party to be added * * Returns: * 0 addparty successful * errno addparty failed - reason indicated * */ int atm_cm_addparty(cop, id, addr) Atm_connection *cop; int id; struct t_atm_sap *addr; { return (0); } /* * XXX * * Arguments: * cop pointer to connection block * id identifier for party to be added * cause pointer to cause of drop * * Returns: * 0 dropparty successful * errno dropparty failed - reason indicated * */ int atm_cm_dropparty(cop, id, cause) Atm_connection *cop; int id; struct t_atm_cause *cause; { return (0); } /* * Release Connection Resources * * Called by the endpoint service in order to terminate an ATM connection * and to release all system resources for the connection. This function * must be called for every allocated connection instance and must only * be called by the connection's owner. * * Arguments: * cop pointer to connection block * cause pointer to cause of release * * Returns: * 0 release successful * errno release failed - reason indicated * */ int atm_cm_release(cop, cause) Atm_connection *cop; struct t_atm_cause *cause; { Atm_connvc *cvp; int s; s = splnet(); /* * First, a quick state validation check */ switch (cop->co_state) { case COS_OUTCONN: case COS_LISTEN: case COS_INACCEPT: case COS_ACTIVE: case COS_CLEAR: /* * Break link to user */ cop->co_toku = NULL; break; case COS_INCONN: (void) splx(s); return (EFAULT); default: panic("atm_cm_release: bogus conn state"); } /* * Check out the VCC state too */ if ((cvp = cop->co_connvc) != NULL) { switch (cvp->cvc_state) { case CVCS_SETUP: case CVCS_INIT: case CVCS_ACCEPT: case CVCS_ACTIVE: break; case CVCS_INCOMING: (void) splx(s); return (EFAULT); case CVCS_CLEAR: (void) splx(s); return (EALREADY); default: panic("atm_cm_release: bogus connvc state"); } /* * If we're the only connection, terminate the VCC */ if ((cop->co_mxh == cop) && (cop->co_next == NULL)) { cvp->cvc_attr.cause.tag = T_ATM_PRESENT; cvp->cvc_attr.cause.v = *cause; atm_cm_closevc(cvp); } } /* * Now get rid of the connection */ atm_cm_closeconn(cop, cause); return (0); } /* * Abort an ATM Connection VCC * * This function allows any non-owner kernel entity to request an * immediate termination of an ATM VCC. This will normally be called * when encountering a catastrophic error condition that cannot be * resolved via the available stack protocols. The connection manager * will schedule the connection's termination, including notifying the * connection owner of the termination. * * This function should only be called by a stack entity instance. After * calling the function, the caller should set a protocol state which just * waits for a _TERM stack command to be delivered. * * Arguments: * cvp pointer to connection VCC block * cause pointer to cause of abort * * Returns: * 0 abort successful * errno abort failed - reason indicated * */ int atm_cm_abort(cvp, cause) Atm_connvc *cvp; struct t_atm_cause *cause; { ATM_DEBUG2("atm_cm_abort: cvp=%p cause=%d\n", cvp, cause->cause_value); /* * Note that we're aborting */ cvp->cvc_flags |= CVCF_ABORTING; switch (cvp->cvc_state) { case CVCS_INIT: /* * In-line code will handle this */ cvp->cvc_attr.cause.tag = T_ATM_PRESENT; cvp->cvc_attr.cause.v = *cause; break; case CVCS_SETUP: case CVCS_ACCEPT: case CVCS_ACTIVE: /* * Schedule connection termination, since we want * to avoid any sequencing interactions */ cvp->cvc_attr.cause.tag = T_ATM_PRESENT; cvp->cvc_attr.cause.v = *cause; CVC_TIMER(cvp, 0); break; case CVCS_REJECT: case CVCS_RELEASE: case CVCS_CLEAR: case CVCS_TERM: /* * Ignore abort, as we're already terminating */ break; default: log(LOG_ERR, "atm_cm_abort: invalid state: cvp=%p, state=%d\n", cvp, cvp->cvc_state); } return (0); } /* * Incoming ATM Call Received * * Called by a signalling manager to indicate that a new call request has * been received. This function will allocate and initialize the connection * manager control blocks and queue this call request. The call request * processing function, atm_cm_procinq(), will be scheduled to perform the * call processing. * * Arguments: * vcp pointer to incoming call's VCC control block * ap pointer to incoming call's attributes * * Returns: * 0 call queuing successful * errno call queuing failed - reason indicated * */ int atm_cm_incoming(vcp, ap) struct vccb *vcp; Atm_attributes *ap; { Atm_connvc *cvp; int s, err; /* * Do some minimal attribute validation */ /* * Must specify a network interface */ if (ap->nif == NULL) return (EINVAL); /* * AAL Attributes */ if ((ap->aal.tag != T_ATM_PRESENT) || ((ap->aal.type != ATM_AAL5) && (ap->aal.type != ATM_AAL3_4))) return (EINVAL); /* * Traffic Descriptor Attributes */ if ((ap->traffic.tag != T_ATM_PRESENT) && (ap->traffic.tag != T_ATM_ABSENT)) return (EINVAL); /* * Broadband Bearer Attributes */ if ((ap->bearer.tag != T_ATM_PRESENT) || ((ap->bearer.v.connection_configuration != T_ATM_1_TO_1) && (ap->bearer.v.connection_configuration != T_ATM_1_TO_MANY))) return (EINVAL); /* * Broadband High Layer Attributes */ if ((ap->bhli.tag != T_ATM_PRESENT) && (ap->bhli.tag != T_ATM_ABSENT)) return (EINVAL); /* * Broadband Low Layer Attributes */ if ((ap->blli.tag_l2 != T_ATM_PRESENT) && (ap->blli.tag_l2 != T_ATM_ABSENT)) return (EINVAL); if ((ap->blli.tag_l3 != T_ATM_PRESENT) && (ap->blli.tag_l3 != T_ATM_ABSENT)) return (EINVAL); /* * Logical Link Control Attributes */ if (ap->llc.tag == T_ATM_PRESENT) return (EINVAL); ap->llc.tag = T_ATM_ANY; /* * Called Party Attributes */ if ((ap->called.tag != T_ATM_PRESENT) || (ap->called.addr.address_format == T_ATM_ABSENT)) return (EINVAL); if (ap->called.tag == T_ATM_ABSENT) { ap->called.addr.address_format = T_ATM_ABSENT; ap->called.addr.address_length = 0; ap->called.subaddr.address_format = T_ATM_ABSENT; ap->called.subaddr.address_length = 0; } /* * Calling Party Attributes */ if ((ap->calling.tag != T_ATM_PRESENT) && (ap->calling.tag != T_ATM_ABSENT)) return (EINVAL); if (ap->calling.tag == T_ATM_ABSENT) { ap->calling.addr.address_format = T_ATM_ABSENT; ap->calling.addr.address_length = 0; ap->calling.subaddr.address_format = T_ATM_ABSENT; ap->calling.subaddr.address_length = 0; } /* * Quality of Service Attributes */ if (ap->qos.tag != T_ATM_PRESENT) return (EINVAL); /* * Transit Network Attributes */ if ((ap->transit.tag != T_ATM_PRESENT) && (ap->transit.tag != T_ATM_ABSENT)) return (EINVAL); /* * Cause Attributes */ if ((ap->cause.tag != T_ATM_PRESENT) && (ap->cause.tag != T_ATM_ABSENT)) return (EINVAL); /* * Get a connection VCC block */ cvp = (Atm_connvc *)atm_allocate(&atm_connvc_pool); if (cvp == NULL) { err = ENOMEM; goto fail; } /* * Initialize the control block */ cvp->cvc_vcc = vcp; cvp->cvc_sigmgr = vcp->vc_pif->pif_sigmgr; cvp->cvc_attr = *ap; cvp->cvc_state = CVCS_INCOMING; /* * Control queue length */ s = splnet(); if (atm_incoming_qlen >= ATM_CALLQ_MAX) { (void) splx(s); err = EBUSY; goto fail; } /* * Queue request and schedule call processing function */ cvp->cvc_flags |= CVCF_INCOMQ; ENQUEUE(cvp, Atm_connvc, cvc_q, atm_incoming_queue); if (atm_incoming_qlen++ == 0) { timeout(atm_cm_procinq, (void *)0, 0); } /* * Link for signalling manager */ vcp->vc_connvc = cvp; (void) splx(s); return (0); fail: /* * Free any resources */ if (cvp) atm_free((caddr_t)cvp); return (err); } /* * VCC Connected Notification * * This function is called by a signalling manager as notification that a * VCC call setup has been successful. * * Arguments: * cvp pointer to connection VCC block * * Returns: * none * */ void atm_cm_connected(cvp) Atm_connvc *cvp; { Atm_connection *cop, *cop2; KBuffer *m; int s, err; s = splnet(); /* * Validate connection vcc */ switch (cvp->cvc_state) { case CVCS_SETUP: /* * Initialize the stack */ cvp->cvc_state = CVCS_INIT; STACK_CALL(atm_stackcmds[cvp->cvc_attr.api].init, cvp->cvc_lower, cvp->cvc_tokl, cvp, cvp->cvc_attr.api_init, 0, err); if (err) panic("atm_cm_connected: init"); if (cvp->cvc_flags & CVCF_ABORTING) { /* * Someone on the stack bailed out...notify all of the * connections and schedule the VCC termination */ cop = cvp->cvc_conn; while (cop) { cop2 = cop->co_next; atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); cop = cop2; } atm_cm_closevc(cvp); (void) splx(s); return; } break; case CVCS_ACCEPT: /* * Stack already initialized */ break; default: panic("atm_cm_connected: connvc state"); } /* * VCC is ready for action */ cvp->cvc_state = CVCS_ACTIVE; /* * Notify all connections that the call has completed */ cop = cvp->cvc_conn; while (cop) { cop2 = cop->co_next; switch (cop->co_state) { case COS_OUTCONN: case COS_INACCEPT: cop->co_state = COS_ACTIVE; (*cop->co_endpt->ep_connected)(cop->co_toku); break; case COS_ACTIVE: /* * May get here if an ep_connected() call (from * above) results in an atm_cm_addllc() call for * the just connected connection. */ break; default: panic("atm_cm_connected: connection state"); } cop = cop2; } (void) splx(s); /* * Input any queued packets */ while ((m = cvp->cvc_rcvq) != NULL) { cvp->cvc_rcvq = KB_QNEXT(m); cvp->cvc_rcvqlen--; KB_QNEXT(m) = NULL; /* * Currently only supported for CPCS API */ atm_cm_cpcs_upper(CPCS_UNITDATA_SIG, cvp, (int)m, 0); } return; } /* * VCC Cleared Notification * * This function is called by a signalling manager as notification that a * VCC call has been cleared. The cause information describing the reason * for the call clearing will be contained in the connection VCC attributes. * * Arguments: * cvp pointer to connection VCC block * * Returns: * none * */ void atm_cm_cleared(cvp) Atm_connvc *cvp; { Atm_connection *cop, *cop2; int s; #ifdef DIAGNOSTIC if ((cvp->cvc_state == CVCS_FREE) || (cvp->cvc_state >= CVCS_CLEAR)) panic("atm_cm_cleared"); #endif cvp->cvc_state = CVCS_CLEAR; s = splnet(); /* * Terminate all connections */ cop = cvp->cvc_conn; while (cop) { cop2 = cop->co_next; atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); cop = cop2; } /* * Clean up connection VCC */ atm_cm_closevc(cvp); (void) splx(s); return; } /* * Process Incoming Call Queue * * This function is scheduled by atm_cm_incoming() in order to process * all the entries on the incoming call queue. * * Arguments: * arg argument passed on timeout() call * * Returns: * none * */ static KTimeout_ret atm_cm_procinq(arg) void *arg; { Atm_connvc *cvp; int cnt = 0, s; /* * Only process incoming calls up to our quota */ while (cnt++ < ATM_CALLQ_MAX) { s = splnet(); /* * Get next awaiting call */ cvp = Q_HEAD(atm_incoming_queue, Atm_connvc); if (cvp == NULL) { (void) splx(s); break; } DEQUEUE(cvp, Atm_connvc, cvc_q, atm_incoming_queue); atm_incoming_qlen--; cvp->cvc_flags &= ~CVCF_INCOMQ; /* * Handle the call */ atm_cm_incall(cvp); (void) splx(s); } /* * If we've expended our quota, reschedule ourselves */ if (cnt >= ATM_CALLQ_MAX) timeout(atm_cm_procinq, (void *)0, 0); } /* * Process Incoming Call * * This function will search through the listening queue and try to find * matching endpoint(s) for the incoming call. If we find any, we will * notify the endpoint service(s) of the incoming call and will then * notify the signalling manager to progress the call to an active status. * * If there are no listeners for the call, the signalling manager will be * notified of a call rejection. * * Called at splnet. * * Arguments: * cvp pointer to connection VCC for incoming call * * Returns: * none * */ static void atm_cm_incall(cvp) Atm_connvc *cvp; { Atm_connection *cop, *lcop, *hcop; Atm_attributes attr; int err; hcop = NULL; lcop = NULL; cop = NULL; attr = cvp->cvc_attr; /* * Look for matching listeners */ while ((lcop = atm_cm_match(&attr, lcop)) != NULL) { if (cop == NULL) { /* * Need a new connection block */ cop = (Atm_connection *) atm_allocate(&atm_connection_pool); if (cop == NULL) { cvp->cvc_attr.cause = atm_cause_tmpl; cvp->cvc_attr.cause.v.cause_value = T_ATM_CAUSE_TEMPORARY_FAILURE; goto fail; } } /* * Initialize connection from listener and incoming call */ cop->co_mxh = NULL; cop->co_state = COS_INCONN; cop->co_mpx = lcop->co_mpx; cop->co_endpt = lcop->co_endpt; cop->co_llc = lcop->co_llc; switch (attr.bearer.v.connection_configuration) { case T_ATM_1_TO_1: cop->co_flags |= COF_P2P; break; case T_ATM_1_TO_MANY: /* Not supported */ cop->co_flags |= COF_P2MP; cvp->cvc_attr.cause = atm_cause_tmpl; cvp->cvc_attr.cause.v.cause_value = T_ATM_CAUSE_BEARER_CAPABILITY_NOT_IMPLEMENTED; goto fail; } /* * Notify endpoint of incoming call */ err = (*cop->co_endpt->ep_incoming) (lcop->co_toku, cop, &cvp->cvc_attr, &cop->co_toku); if (err == 0) { /* * Endpoint has accepted the call * * Setup call attributes */ if (hcop == NULL) { cvp->cvc_attr.api = lcop->co_lattr->api; cvp->cvc_attr.api_init = lcop->co_lattr->api_init; cvp->cvc_attr.llc = lcop->co_lattr->llc; } cvp->cvc_attr.headin = MAX(cvp->cvc_attr.headin, lcop->co_lattr->headin); /* * Setup connection info and queueing */ cop->co_state = COS_INACCEPT; cop->co_connvc = cvp; LINK2TAIL(cop, Atm_connection, hcop, co_next); cop->co_mxh = hcop; /* * Need a new connection block next time around */ cop = NULL; } else { /* * Endpoint refuses call */ goto fail; } } /* * We're done looking for listeners */ if (hcop) { /* * Someone actually wants the call, so notify * the signalling manager to continue */ cvp->cvc_flags |= CVCF_CONNQ; ENQUEUE(cvp, Atm_connvc, cvc_q, atm_connection_queue); if (atm_cm_accept(cvp, hcop)) goto fail; } else { /* * Nobody around to take the call */ cvp->cvc_attr.cause = atm_cause_tmpl; cvp->cvc_attr.cause.v.cause_value = T_ATM_CAUSE_INCOMPATIBLE_DESTINATION; goto fail; } /* * Clean up loose ends */ if (cop) atm_free((caddr_t)cop); /* * Call has been accepted */ return; fail: /* * Call failed - notify any endpoints of the call failure */ /* * Clean up loose ends */ if (cop) atm_free((caddr_t)cop); if (cvp->cvc_attr.cause.tag != T_ATM_PRESENT) { cvp->cvc_attr.cause = atm_cause_tmpl; cvp->cvc_attr.cause.v.cause_value = T_ATM_CAUSE_UNSPECIFIED_NORMAL; } cop = hcop; while (cop) { Atm_connection *cop2 = cop->co_next; atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); cop = cop2; } /* * Tell the signalling manager to reject the call */ atm_cm_closevc(cvp); return; } /* * Accept an Incoming ATM Call * * Some endpoint service(s) wants to accept an incoming call, so the * signalling manager will be notified to attempt to progress the call * to an active status. * * If the signalling manager indicates that connection activation has * been immediately successful, then all of the endpoints will be notified * that the connection is ready for data transmission. * * If the return indicates that connection activation is still in progress, * then the endpoints must wait for notification from the Connection Manager * indicating the final status of the call setup. If the call setup completes * successfully, then a "call connected" notification will be sent to the * endpoints by the Connection Manager. If the call setup fails, then the * endpoints will receive a "call cleared" notification. * * Called at splnet. * * Arguments: * cvp pointer to connection VCC for incoming call * cop pointer to head of accepted connections * * Returns: * 0 connection has been successfully activated * errno accept failed - reason indicated * */ static int atm_cm_accept(cvp, cop) Atm_connvc *cvp; Atm_connection *cop; { struct stack_list sl; void (*upf)(int, void *, int, int); int sli, err, err2; /* * Link vcc to connections */ cvp->cvc_conn = cop; /* * Initialize stack list index */ sli = 0; /* * Check out Data API */ switch (cvp->cvc_attr.api) { case CMAPI_CPCS: upf = atm_cm_cpcs_upper; break; case CMAPI_SAAL: sl.sl_sap[sli++] = SAP_SSCF_UNI; sl.sl_sap[sli++] = SAP_SSCOP; upf = atm_cm_saal_upper; break; case CMAPI_SSCOP: sl.sl_sap[sli++] = SAP_SSCOP; upf = atm_cm_sscop_upper; break; default: upf = NULL; } /* * AAL Attributes */ switch (cvp->cvc_attr.aal.type) { case ATM_AAL5: sl.sl_sap[sli++] = SAP_CPCS_AAL5; sl.sl_sap[sli++] = SAP_SAR_AAL5; sl.sl_sap[sli++] = SAP_ATM; break; case ATM_AAL3_4: sl.sl_sap[sli++] = SAP_CPCS_AAL3_4; sl.sl_sap[sli++] = SAP_SAR_AAL3_4; sl.sl_sap[sli++] = SAP_ATM; break; } /* * Terminate stack list */ sl.sl_sap[sli] = 0; /* * Create a service stack */ err = atm_create_stack(cvp, &sl, upf); if (err) { goto done; } /* * Let the signalling manager finish the VCC activation */ switch ((*cvp->cvc_sigmgr->sm_accept)(cvp->cvc_vcc, &err)) { case CALL_PROCEEDING: /* * Note that we're not finished yet */ err = EINPROGRESS; /* FALLTHRU */ case CALL_CONNECTED: /* * Initialize the stack now, even if the call isn't totally * active yet. We want to avoid the delay between getting * the "call connected" event and actually notifying the * adapter to accept cells on the new VCC - if the delay is * too long, then we end up dropping the first pdus sent by * the caller. */ cvp->cvc_state = CVCS_INIT; STACK_CALL(atm_stackcmds[cvp->cvc_attr.api].init, cvp->cvc_lower, cvp->cvc_tokl, cvp, cvp->cvc_attr.api_init, 0, err2); if (err2) panic("atm_cm_accept: init"); if (cvp->cvc_flags & CVCF_ABORTING) { /* * Someone on the stack bailed out...schedule the * VCC and stack termination */ err = ECONNABORTED; } else { /* * Everything looks fine from here */ if (err) cvp->cvc_state = CVCS_ACCEPT; else cvp->cvc_state = CVCS_ACTIVE; } break; case CALL_FAILED: /* * Terminate stack and clean up before we leave */ cvp->cvc_state = CVCS_CLEAR; break; default: panic("atm_cm_accept: accept"); } done: if (err == 0) { /* * Call has been connected, notify endpoints */ while (cop) { Atm_connection *cop2 = cop->co_next; cop->co_state = COS_ACTIVE; (*cop->co_endpt->ep_connected)(cop->co_toku); cop = cop2; } } else if (err == EINPROGRESS) { /* * Call is still in progress, endpoint must wait */ err = 0; } else { /* * Let caller know we failed */ cvp->cvc_attr.cause = atm_cause_tmpl; cvp->cvc_attr.cause.v.cause_value = T_ATM_CAUSE_UNSPECIFIED_RESOURCE_UNAVAILABLE; } return (err); } /* * Match Attributes on Listening Queue * * This function will attempt to match the supplied connection attributes * with one of the registered attributes in the listening queue. The pcop * argument may be supplied in order to allow multiple listeners to share * an incoming call (if supported by the listeners). * * Called at splnet. * * Arguments: * ap pointer to attributes to be matched * pcop pointer to the previously matched connection * * Returns: * addr connection with which a match was found * 0 no match found * */ Atm_connection * atm_cm_match(ap, pcop) Atm_attributes *ap; Atm_connection *pcop; { Atm_connection *cop; Atm_attributes *lap; /* * If we've already matched a listener... */ if (pcop) { /* * Make sure already matched listener supports sharing */ if ((pcop->co_mpx != ATM_ENC_LLC) || ((pcop->co_llc.v.flags & T_ATM_LLC_SHARING) == 0)) return (NULL); /* * Position ourselves after the matched entry */ for (cop = atm_listen_queue; cop; cop = cop->co_next) { if (cop == pcop) { cop = pcop->co_next; break; } } } else { /* * Start search at top of listening queue */ cop = atm_listen_queue; } /* * Search through listening queue */ for (; cop; cop = cop->co_next) { lap = cop->co_lattr; /* * If we're trying to share, check that this entry allows it */ if (pcop) { if ((cop->co_mpx != ATM_ENC_LLC) || ((cop->co_llc.v.flags & T_ATM_LLC_SHARING) == 0)) continue; } /* * ALL "matchable" attributes must match */ /* * BHLI */ if (lap->bhli.tag == T_ATM_ABSENT) { if (ap->bhli.tag == T_ATM_PRESENT) continue; } else if (lap->bhli.tag == T_ATM_PRESENT) { if (ap->bhli.tag == T_ATM_ABSENT) continue; if (ap->bhli.tag == T_ATM_PRESENT) if (bcmp(&lap->bhli.v, &ap->bhli.v, sizeof(struct t_atm_bhli))) continue; } /* * BLLI Layer 2 */ if (lap->blli.tag_l2 == T_ATM_ABSENT) { if (ap->blli.tag_l2 == T_ATM_PRESENT) continue; } else if (lap->blli.tag_l2 == T_ATM_PRESENT) { if (ap->blli.tag_l2 == T_ATM_ABSENT) continue; if (ap->blli.tag_l2 == T_ATM_PRESENT) { if (bcmp(&lap->blli.v.layer_2_protocol.ID, &ap->blli.v.layer_2_protocol.ID, sizeof( ap->blli.v.layer_2_protocol.ID))) continue; } } /* * BLLI Layer 3 */ if (lap->blli.tag_l3 == T_ATM_ABSENT) { if (ap->blli.tag_l3 == T_ATM_PRESENT) continue; } else if (lap->blli.tag_l3 == T_ATM_PRESENT) { if (ap->blli.tag_l3 == T_ATM_ABSENT) continue; if (ap->blli.tag_l3 == T_ATM_PRESENT) { if (bcmp(&lap->blli.v.layer_3_protocol.ID, &ap->blli.v.layer_3_protocol.ID, sizeof( ap->blli.v.layer_3_protocol.ID))) continue; } } /* * LLC */ if (lap->llc.tag == T_ATM_ABSENT) { if (ap->llc.tag == T_ATM_PRESENT) continue; } else if (lap->llc.tag == T_ATM_PRESENT) { if (ap->llc.tag == T_ATM_ABSENT) continue; if (ap->llc.tag == T_ATM_PRESENT) { int i = MIN(lap->llc.v.llc_len, ap->llc.v.llc_len); if (bcmp(lap->llc.v.llc_info, ap->llc.v.llc_info, i)) continue; } } /* * AAL */ if (lap->aal.tag == T_ATM_ABSENT) { if (ap->aal.tag == T_ATM_PRESENT) continue; } else if (lap->aal.tag == T_ATM_PRESENT) { if (ap->aal.tag == T_ATM_ABSENT) continue; if (ap->aal.tag == T_ATM_PRESENT) { if (lap->aal.type != ap->aal.type) continue; if (lap->aal.type == ATM_AAL5) { if (lap->aal.v.aal5.SSCS_type != ap->aal.v.aal5.SSCS_type) continue; } else { if (lap->aal.v.aal4.SSCS_type != ap->aal.v.aal4.SSCS_type) continue; } } } /* * Called Party */ if (lap->called.tag == T_ATM_ABSENT) { if (ap->called.tag == T_ATM_PRESENT) continue; } else if (lap->called.tag == T_ATM_PRESENT) { if (ap->called.tag == T_ATM_ABSENT) continue; if (ap->called.tag == T_ATM_PRESENT) { if ((!ATM_ADDR_EQUAL(&lap->called.addr, &ap->called.addr)) || (!ATM_ADDR_EQUAL(&lap->called.subaddr, &ap->called.subaddr))) continue; } } /* * Found a full match - return it */ break; } return (cop); } /* * Find Shareable LLC VCC * * Given a endpoint-supplied connection attribute using LLC multiplexing, * this function will attempt to locate an existing connection which meets * the requirements of the supplied attributes. * * Called at splnet. * * Arguments: * ap pointer to requested attributes * * Returns: * addr shareable LLC connection VCC * 0 no shareable VCC available * */ static Atm_connvc * atm_cm_share_llc(ap) Atm_attributes *ap; { Atm_connection *cop; Atm_connvc *cvp; /* * Is requestor willing to share? */ if ((ap->llc.v.flags & T_ATM_LLC_SHARING) == 0) return (NULL); /* * Try to find a shareable connection */ for (cvp = Q_HEAD(atm_connection_queue, Atm_connvc); cvp; cvp = Q_NEXT(cvp, Atm_connvc, cvc_q)) { /* * Dont use terminating connections */ switch (cvp->cvc_state) { case CVCS_SETUP: case CVCS_ACCEPT: case CVCS_ACTIVE: break; default: continue; } /* * Is connection LLC and shareable? */ if ((cvp->cvc_attr.llc.tag != T_ATM_PRESENT) || ((cvp->cvc_attr.llc.v.flags & T_ATM_LLC_SHARING) == 0)) continue; /* * Match requested attributes with existing connection */ if (ap->nif != cvp->cvc_attr.nif) continue; if ((ap->api != cvp->cvc_attr.api) || (ap->api_init != cvp->cvc_attr.api_init)) continue; /* * Remote Party */ if (cvp->cvc_flags & CVCF_CALLER) { if ((!ATM_ADDR_EQUAL(&ap->called.addr, &cvp->cvc_attr.called.addr)) || (!ATM_ADDR_EQUAL(&ap->called.subaddr, &cvp->cvc_attr.called.subaddr))) continue; } else { if (cvp->cvc_attr.calling.tag != T_ATM_PRESENT) continue; if ((!ATM_ADDR_EQUAL(&ap->called.addr, &cvp->cvc_attr.calling.addr)) || (!ATM_ADDR_EQUAL(&ap->called.subaddr, &cvp->cvc_attr.calling.subaddr))) continue; } /* * AAL */ if (ap->aal.type == ATM_AAL5) { struct t_atm_aal5 *ap5, *cv5; ap5 = &ap->aal.v.aal5; cv5 = &cvp->cvc_attr.aal.v.aal5; if ((cvp->cvc_attr.aal.type != ATM_AAL5) || (ap5->SSCS_type != cv5->SSCS_type)) continue; if (cvp->cvc_flags & CVCF_CALLER) { if (ap5->forward_max_SDU_size > cv5->forward_max_SDU_size) continue; } else { if (ap5->forward_max_SDU_size > cv5->backward_max_SDU_size) continue; } } else { struct t_atm_aal4 *ap4, *cv4; ap4 = &ap->aal.v.aal4; cv4 = &cvp->cvc_attr.aal.v.aal4; if ((cvp->cvc_attr.aal.type != ATM_AAL3_4) || (ap4->SSCS_type != cv4->SSCS_type)) continue; if (cvp->cvc_flags & CVCF_CALLER) { if (ap4->forward_max_SDU_size > cv4->forward_max_SDU_size) continue; } else { if (ap4->forward_max_SDU_size > cv4->backward_max_SDU_size) continue; } } /* * Traffic Descriptor */ if ((ap->traffic.tag != T_ATM_PRESENT) || (cvp->cvc_attr.traffic.tag != T_ATM_PRESENT) || (ap->traffic.v.best_effort != T_YES) || (cvp->cvc_attr.traffic.v.best_effort != T_YES)) continue; /* * Broadband Bearer */ if (ap->bearer.v.connection_configuration != cvp->cvc_attr.bearer.v.connection_configuration) continue; /* * QOS */ if (cvp->cvc_flags & CVCF_CALLER) { if ((ap->qos.v.forward.qos_class != cvp->cvc_attr.qos.v.forward.qos_class) || (ap->qos.v.backward.qos_class != cvp->cvc_attr.qos.v.backward.qos_class)) continue; } else { if ((ap->qos.v.forward.qos_class != cvp->cvc_attr.qos.v.backward.qos_class) || (ap->qos.v.backward.qos_class != cvp->cvc_attr.qos.v.forward.qos_class)) continue; } /* * The new LLC header must also be unique for this VCC */ for (cop = cvp->cvc_conn; cop; cop = cop->co_next) { int i = MIN(ap->llc.v.llc_len, cop->co_llc.v.llc_len); if (bcmp(ap->llc.v.llc_info, cop->co_llc.v.llc_info, i) == 0) break; } /* * If no header overlaps, then we're done */ if (cop == NULL) break; } return (cvp); } /* * Close Connection * * This function will terminate a connection, including notifying the * user, if necessary, and freeing up control block memory. The caller * is responsible for managing the connection VCC. * * Called at splnet. * * Arguments: * cop pointer to connection block * cause pointer to cause of close * * Returns: * none * */ static void atm_cm_closeconn(cop, cause) Atm_connection *cop; struct t_atm_cause *cause; { /* * Decide whether user needs notification */ switch (cop->co_state) { case COS_OUTCONN: case COS_LISTEN: case COS_INCONN: case COS_INACCEPT: case COS_ACTIVE: /* * Yup, let 'em know connection is gone */ if (cop->co_toku) (*cop->co_endpt->ep_cleared)(cop->co_toku, cause); break; case COS_CLEAR: /* * Nope,they should know already */ break; default: panic("atm_cm_closeconn: bogus state"); } /* * Unlink connection from its queues */ switch (cop->co_state) { case COS_LISTEN: uma_zfree(atm_attributes_zone, cop->co_lattr); UNLINK(cop, Atm_connection, atm_listen_queue, co_next); break; default: /* * Remove connection from multiplexor queue */ if (cop->co_mxh != cop) { /* * Connection is down the chain, just unlink it */ UNLINK(cop, Atm_connection, cop->co_mxh, co_next); } else if (cop->co_next != NULL) { /* * Connection is at the head of a non-singleton chain, * so unlink and reset the chain head */ Atm_connection *t, *nhd; t = nhd = cop->co_next; while (t) { t->co_mxh = nhd; t = t->co_next; } if (nhd->co_connvc) nhd->co_connvc->cvc_conn = nhd; } } /* * Free the connection block */ cop->co_state = COS_FREE; atm_free((caddr_t)cop); return; } /* * Close Connection VCC * * This function will terminate a connection VCC, including releasing the * the call to the signalling manager, terminating the VCC protocol stack, * and freeing up control block memory. * * Called at splnet. * * Arguments: * cvp pointer to connection VCC block * * Returns: * none * */ static void atm_cm_closevc(cvp) Atm_connvc *cvp; { int err; /* * Break links with the connection block */ cvp->cvc_conn = NULL; /* * Cancel any running timer */ CVC_CANCEL(cvp); /* * Free queued packets */ while (cvp->cvc_rcvq) { KBuffer *m; m = cvp->cvc_rcvq; cvp->cvc_rcvq = KB_QNEXT(m); KB_QNEXT(m) = NULL; KB_FREEALL(m); } /* * Unlink from any queues */ if (cvp->cvc_flags & CVCF_INCOMQ) { DEQUEUE(cvp, Atm_connvc, cvc_q, atm_incoming_queue); atm_incoming_qlen--; cvp->cvc_flags &= ~CVCF_INCOMQ; } else if (cvp->cvc_flags & CVCF_CONNQ) { DEQUEUE(cvp, Atm_connvc, cvc_q, atm_connection_queue); cvp->cvc_flags &= ~CVCF_CONNQ; } /* * Release the signalling call */ switch (cvp->cvc_state) { case CVCS_SETUP: case CVCS_INIT: case CVCS_ACCEPT: case CVCS_ACTIVE: case CVCS_RELEASE: if (cvp->cvc_vcc) { cvp->cvc_state = CVCS_RELEASE; switch ((*cvp->cvc_sigmgr->sm_release) (cvp->cvc_vcc, &err)) { case CALL_CLEARED: /* * Looks good so far... */ break; case CALL_PROCEEDING: /* * We'll have to wait for the call to clear */ return; case CALL_FAILED: /* * If there's a memory shortage, retry later. * Otherwise who knows what's going on.... */ if ((err == ENOMEM) || (err == ENOBUFS)) { CVC_TIMER(cvp, 1 * ATM_HZ); return; } log(LOG_ERR, "atm_cm_closevc: release %d\n", err); break; } } break; case CVCS_INCOMING: case CVCS_REJECT: if (cvp->cvc_vcc) { cvp->cvc_state = CVCS_REJECT; switch ((*cvp->cvc_sigmgr->sm_reject) (cvp->cvc_vcc, &err)) { case CALL_CLEARED: /* * Looks good so far... */ break; case CALL_FAILED: /* * If there's a memory shortage, retry later. * Otherwise who knows what's going on.... */ if ((err == ENOMEM) || (err == ENOBUFS)) { CVC_TIMER(cvp, 1 * ATM_HZ); return; } log(LOG_ERR, "atm_cm_closevc: reject %d\n", err); break; } } break; case CVCS_CLEAR: case CVCS_TERM: /* * No need for anything here */ break; default: panic("atm_cm_closevc: bogus state"); } /* * Now terminate the stack */ if (cvp->cvc_tokl) { cvp->cvc_state = CVCS_TERM; /* * Wait until stack is unwound before terminating */ if ((cvp->cvc_downcnt > 0) || (cvp->cvc_upcnt > 0)) { CVC_TIMER(cvp, 0); return; } STACK_CALL(atm_stackcmds[cvp->cvc_attr.api].term, cvp->cvc_lower, cvp->cvc_tokl, cvp, 0, 0, err); cvp->cvc_tokl = NULL; } /* * Let signalling manager finish up */ cvp->cvc_state = CVCS_FREE; if (cvp->cvc_vcc) { (void) (*cvp->cvc_sigmgr->sm_free)(cvp->cvc_vcc); } /* * Finally, free our own control blocks */ atm_free((caddr_t)cvp); return; } /* * Process a Connection VCC timeout * * Called when a previously scheduled cvc control block timer expires. * Processing will be based on the current cvc state. * * Called at splnet. * * Arguments: * tip pointer to cvc timer control block * * Returns: * none * */ static void atm_cm_timeout(tip) struct atm_time *tip; { Atm_connection *cop, *cop2; Atm_connvc *cvp; /* * Back-off to cvc control block */ cvp = (Atm_connvc *) ((caddr_t)tip - (int)(&((Atm_connvc *)0)->cvc_time)); /* * Process timeout based on protocol state */ switch (cvp->cvc_state) { case CVCS_SETUP: case CVCS_ACCEPT: case CVCS_ACTIVE: /* * Handle VCC abort */ if ((cvp->cvc_flags & CVCF_ABORTING) == 0) goto logerr; /* * Terminate all connections */ cop = cvp->cvc_conn; while (cop) { cop2 = cop->co_next; atm_cm_closeconn(cop, &cvp->cvc_attr.cause.v); cop = cop2; } /* * Terminate VCC */ atm_cm_closevc(cvp); break; case CVCS_REJECT: case CVCS_RELEASE: case CVCS_TERM: /* * Retry failed operation */ atm_cm_closevc(cvp); break; default: logerr: log(LOG_ERR, "atm_cm_timeout: invalid state: cvp=%p, state=%d\n", cvp, cvp->cvc_state); } } /* * CPCS User Control Commands * * This function is called by an endpoint user to pass a control command * across a CPCS data API. Mostly we just send these down the stack. * * Arguments: * cmd stack command code * cop pointer to connection block * arg argument * * Returns: * 0 command output successful * errno output failed - reason indicated * */ int atm_cm_cpcs_ctl(cmd, cop, arg) int cmd; Atm_connection *cop; void *arg; { Atm_connvc *cvp; int err = 0; /* * Validate connection state */ if (cop->co_state != COS_ACTIVE) { err = EFAULT; goto done; } cvp = cop->co_connvc; if (cvp->cvc_state != CVCS_ACTIVE) { err = EFAULT; goto done; } if (cvp->cvc_attr.api != CMAPI_CPCS) { err = EFAULT; goto done; } switch (cmd) { default: err = EINVAL; } done: return (err); } /* * CPCS Data Output * * This function is called by an endpoint user to output a data packet * across a CPCS data API. After we've validated the connection state, the * packet will be encapsulated (if necessary) and sent down the data stack. * * Arguments: * cop pointer to connection block * m pointer to packet buffer chain to be output * * Returns: * 0 packet output successful * errno output failed - reason indicated * */ int atm_cm_cpcs_data(cop, m) Atm_connection *cop; KBuffer *m; { Atm_connvc *cvp; struct attr_llc *llcp; int err, space; void *bp; /* * Validate connection state */ if (cop->co_state != COS_ACTIVE) { err = EFAULT; goto done; } cvp = cop->co_connvc; if (cvp->cvc_state != CVCS_ACTIVE) { err = EFAULT; goto done; } if (cvp->cvc_attr.api != CMAPI_CPCS) { err = EFAULT; goto done; } /* * Add any packet encapsulation */ switch (cop->co_mpx) { case ATM_ENC_NULL: /* * None needed... */ break; case ATM_ENC_LLC: /* * Need to add an LLC header */ llcp = &cop->co_llc; /* * See if there's room to add LLC header to front of packet. */ KB_HEADROOM(m, space); if (space < llcp->v.llc_len) { KBuffer *n; /* * We have to allocate another buffer and tack it * onto the front of the packet */ KB_ALLOCPKT(n, llcp->v.llc_len, KB_F_NOWAIT, KB_T_HEADER); if (n == 0) { err = ENOMEM; goto done; } KB_TAILALIGN(n, llcp->v.llc_len); KB_LINKHEAD(n, m); m = n; } else { /* * Header fits, just adjust buffer controls */ KB_HEADADJ(m, llcp->v.llc_len); } /* * Add the LLC header */ KB_DATASTART(m, bp, void *); bcopy(llcp->v.llc_info, bp, llcp->v.llc_len); KB_PLENADJ(m, llcp->v.llc_len); break; default: panic("atm_cm_cpcs_data: mpx"); } /* * Finally, we can send the packet on its way */ STACK_CALL(CPCS_UNITDATA_INV, cvp->cvc_lower, cvp->cvc_tokl, cvp, (int)m, 0, err); done: return (err); } /* * Process CPCS Stack Commands * * This is the top of the CPCS API data stack. All upward stack commands * for the CPCS data API will be received and processed here. * * Arguments: * cmd stack command code * tok session token (pointer to connection VCC control block) * arg1 argument 1 * arg2 argument 2 * * Returns: * none * */ static void atm_cm_cpcs_upper(cmd, tok, arg1, arg2) int cmd; void *tok; int arg1; int arg2; { Atm_connection *cop; Atm_connvc *cvp = tok; KBuffer *m; void *bp; int s; switch (cmd) { case CPCS_UNITDATA_SIG: /* * Input data packet */ m = (KBuffer *)arg1; if (cvp->cvc_state != CVCS_ACTIVE) { if (cvp->cvc_state == CVCS_ACCEPT) { KBuffer *n; /* * Queue up any packets received before sigmgr * notifies us of incoming call completion */ if (cvp->cvc_rcvqlen >= CVC_RCVQ_MAX) { KB_FREEALL(m); atm_cm_stat.cms_rcvconnvc++; return; } KB_QNEXT(m) = NULL; if (cvp->cvc_rcvq == NULL) { cvp->cvc_rcvq = m; } else { for (n = cvp->cvc_rcvq; KB_QNEXT(n) != NULL; n = KB_QNEXT(n)) ; KB_QNEXT(n) = m; } cvp->cvc_rcvqlen++; return; } else { KB_FREEALL(m); atm_cm_stat.cms_rcvconnvc++; return; } } /* * Locate packet's connection */ cop = cvp->cvc_conn; switch (cop->co_mpx) { case ATM_ENC_NULL: /* * We're already there... */ break; case ATM_ENC_LLC: /* * Find connection with matching LLC header */ if (KB_LEN(m) < T_ATM_LLC_MAX_LEN) { KB_PULLUP(m, T_ATM_LLC_MAX_LEN, m); if (m == 0) { atm_cm_stat.cms_llcdrop++; return; } } KB_DATASTART(m, bp, void *); s = splnet(); while (cop) { if (bcmp(bp, cop->co_llc.v.llc_info, cop->co_llc.v.llc_len) == 0) break; cop = cop->co_next; } (void) splx(s); if (cop == NULL) { /* * No connected user for this LLC */ KB_FREEALL(m); atm_cm_stat.cms_llcid++; return; } /* * Strip off the LLC header */ KB_HEADADJ(m, -cop->co_llc.v.llc_len); KB_PLENADJ(m, -cop->co_llc.v.llc_len); break; default: panic("atm_cm_cpcs_upper: mpx"); } /* * We've found our connection, so hand the packet off */ if (cop->co_state != COS_ACTIVE) { KB_FREEALL(m); atm_cm_stat.cms_rcvconn++; return; } (*cop->co_endpt->ep_cpcs_data)(cop->co_toku, m); break; case CPCS_UABORT_SIG: case CPCS_PABORT_SIG: /* * We don't support these (yet), so just fall thru... */ default: log(LOG_ERR, "atm_cm_cpcs_upper: unknown cmd 0x%x\n", cmd); } } /* * SAAL User Control Commands * * This function is called by an endpoint user to pass a control command * across a SAAL data API. Mostly we just send these down the stack. * * Arguments: * cmd stack command code * cop pointer to connection block * arg argument * * Returns: * 0 command output successful * errno output failed - reason indicated * */ int atm_cm_saal_ctl(cmd, cop, arg) int cmd; Atm_connection *cop; void *arg; { Atm_connvc *cvp; int err = 0; /* * Validate connection state */ if (cop->co_state != COS_ACTIVE) { err = EFAULT; goto done; } cvp = cop->co_connvc; if (cvp->cvc_state != CVCS_ACTIVE) { err = EFAULT; goto done; } if (cvp->cvc_attr.api != CMAPI_SAAL) { err = EFAULT; goto done; } switch (cmd) { case SSCF_UNI_ESTABLISH_REQ: case SSCF_UNI_RELEASE_REQ: /* * Pass command down the stack */ STACK_CALL(cmd, cvp->cvc_lower, cvp->cvc_tokl, cvp, (int)arg, 0, err); break; default: err = EINVAL; } done: return (err); } /* * SAAL Data Output * * This function is called by an endpoint user to output a data packet * across a SAAL data API. After we've validated the connection state, * the packet will be sent down the data stack. * * Arguments: * cop pointer to connection block * m pointer to packet buffer chain to be output * * Returns: * 0 packet output successful * errno output failed - reason indicated * */ int atm_cm_saal_data(cop, m) Atm_connection *cop; KBuffer *m; { Atm_connvc *cvp; int err; /* * Validate connection state */ if (cop->co_state != COS_ACTIVE) { err = EFAULT; goto done; } cvp = cop->co_connvc; if (cvp->cvc_state != CVCS_ACTIVE) { err = EFAULT; goto done; } if (cvp->cvc_attr.api != CMAPI_SAAL) { err = EFAULT; goto done; } /* * Finally, we can send the packet on its way */ STACK_CALL(SSCF_UNI_DATA_REQ, cvp->cvc_lower, cvp->cvc_tokl, cvp, (int)m, 0, err); done: return (err); } /* * Process SAAL Stack Commands * * This is the top of the SAAL API data stack. All upward stack commands * for the SAAL data API will be received and processed here. * * Arguments: * cmd stack command code * tok session token (pointer to connection VCC control block) * arg1 argument 1 * arg2 argument 2 * * Returns: * none * */ static void atm_cm_saal_upper(cmd, tok, arg1, arg2) int cmd; void *tok; int arg1; int arg2; { Atm_connection *cop; Atm_connvc *cvp = tok; switch (cmd) { case SSCF_UNI_ESTABLISH_IND: case SSCF_UNI_ESTABLISH_CNF: case SSCF_UNI_RELEASE_IND: case SSCF_UNI_RELEASE_CNF: /* * Control commands */ cop = cvp->cvc_conn; if (cvp->cvc_state != CVCS_ACTIVE) break; if (cop->co_state != COS_ACTIVE) break; (*cop->co_endpt->ep_saal_ctl)(cmd, cop->co_toku, (void *)arg1); break; case SSCF_UNI_DATA_IND: /* * User data */ cop = cvp->cvc_conn; if (cvp->cvc_state != CVCS_ACTIVE) { atm_cm_stat.cms_rcvconnvc++; KB_FREEALL((KBuffer *)arg1); break; } if (cop->co_state != COS_ACTIVE) { atm_cm_stat.cms_rcvconn++; KB_FREEALL((KBuffer *)arg1); break; } (*cop->co_endpt->ep_saal_data)(cop->co_toku, (KBuffer *)arg1); break; case SSCF_UNI_UNITDATA_IND: /* * Not supported */ KB_FREEALL((KBuffer *)arg1); /* FALLTHRU */ default: log(LOG_ERR, "atm_cm_saal_upper: unknown cmd 0x%x\n", cmd); } } /* * SSCOP User Control Commands * * This function is called by an endpoint user to pass a control command * across a SSCOP data API. Mostly we just send these down the stack. * * Arguments: * cmd stack command code * cop pointer to connection block * arg1 argument * arg2 argument * * Returns: * 0 command output successful * errno output failed - reason indicated * */ int atm_cm_sscop_ctl(cmd, cop, arg1, arg2) int cmd; Atm_connection *cop; void *arg1; void *arg2; { Atm_connvc *cvp; int err = 0; /* * Validate connection state */ if (cop->co_state != COS_ACTIVE) { err = EFAULT; goto done; } cvp = cop->co_connvc; if (cvp->cvc_state != CVCS_ACTIVE) { err = EFAULT; goto done; } if (cvp->cvc_attr.api != CMAPI_SSCOP) { err = EFAULT; goto done; } switch (cmd) { case SSCOP_ESTABLISH_REQ: case SSCOP_ESTABLISH_RSP: case SSCOP_RELEASE_REQ: case SSCOP_RESYNC_REQ: case SSCOP_RESYNC_RSP: case SSCOP_RECOVER_RSP: case SSCOP_RETRIEVE_REQ: /* * Pass command down the stack */ STACK_CALL(cmd, cvp->cvc_lower, cvp->cvc_tokl, cvp, (int)arg1, (int)arg2, err); break; default: err = EINVAL; } done: return (err); } /* * SSCOP Data Output * * This function is called by an endpoint user to output a data packet * across a SSCOP data API. After we've validated the connection state, * the packet will be encapsulated and sent down the data stack. * * Arguments: * cop pointer to connection block * m pointer to packet buffer chain to be output * * Returns: * 0 packet output successful * errno output failed - reason indicated * */ int atm_cm_sscop_data(cop, m) Atm_connection *cop; KBuffer *m; { Atm_connvc *cvp; int err; /* * Validate connection state */ if (cop->co_state != COS_ACTIVE) { err = EFAULT; goto done; } cvp = cop->co_connvc; if (cvp->cvc_state != CVCS_ACTIVE) { err = EFAULT; goto done; } if (cvp->cvc_attr.api != CMAPI_SSCOP) { err = EFAULT; goto done; } /* * Finally, we can send the packet on its way */ STACK_CALL(SSCOP_DATA_REQ, cvp->cvc_lower, cvp->cvc_tokl, cvp, (int)m, 0, err); done: return (err); } /* * Process SSCOP Stack Commands * * This is the top of the SSCOP API data stack. All upward stack commands * for the SSCOP data API will be received and processed here. * * Arguments: * cmd stack command code * tok session token (pointer to connection VCC control block) * arg1 argument 1 * arg2 argument 2 * * Returns: * none * */ static void atm_cm_sscop_upper(cmd, tok, arg1, arg2) int cmd; void *tok; int arg1; int arg2; { Atm_connection *cop; Atm_connvc *cvp = tok; switch (cmd) { case SSCOP_ESTABLISH_IND: case SSCOP_ESTABLISH_CNF: case SSCOP_RELEASE_IND: case SSCOP_RESYNC_IND: /* * Control commands */ cop = cvp->cvc_conn; if ((cvp->cvc_state != CVCS_ACTIVE) || (cop->co_state != COS_ACTIVE)) { KB_FREEALL((KBuffer *)arg1); break; } (*cop->co_endpt->ep_sscop_ctl) (cmd, cop->co_toku, (void *)arg1, (void *)arg2); break; case SSCOP_RELEASE_CNF: case SSCOP_RESYNC_CNF: case SSCOP_RECOVER_IND: case SSCOP_RETRIEVE_IND: case SSCOP_RETRIEVECMP_IND: /* * Control commands */ cop = cvp->cvc_conn; if ((cvp->cvc_state != CVCS_ACTIVE) || (cop->co_state != COS_ACTIVE)) break; (*cop->co_endpt->ep_sscop_ctl) (cmd, cop->co_toku, (void *)arg1, (void *)arg2); break; case SSCOP_DATA_IND: /* * User data */ cop = cvp->cvc_conn; if (cvp->cvc_state != CVCS_ACTIVE) { atm_cm_stat.cms_rcvconnvc++; KB_FREEALL((KBuffer *)arg1); break; } if (cop->co_state != COS_ACTIVE) { atm_cm_stat.cms_rcvconn++; KB_FREEALL((KBuffer *)arg1); break; } (*cop->co_endpt->ep_sscop_data) (cop->co_toku, (KBuffer *)arg1, arg2); break; case SSCOP_UNITDATA_IND: /* * Not supported */ KB_FREEALL((KBuffer *)arg1); /* FALLTHRU */ default: log(LOG_ERR, "atm_cm_sscop_upper: unknown cmd 0x%x\n", cmd); } } /* * Register an ATM Endpoint Service * * Every ATM endpoint service must register itself here before it can * issue or receive any connection requests. * * Arguments: * epp pointer to endpoint definition structure * * Returns: * 0 registration successful * errno registration failed - reason indicated * */ int atm_endpoint_register(epp) Atm_endpoint *epp; { int s = splnet(); /* * See if we need to be initialized */ if (!atm_init) atm_initialize(); /* * Validate endpoint */ if (epp->ep_id > ENDPT_MAX) { (void) splx(s); return (EINVAL); } if (atm_endpoints[epp->ep_id] != NULL) { (void) splx(s); return (EEXIST); } /* * Add endpoint to list */ atm_endpoints[epp->ep_id] = epp; (void) splx(s); return (0); } /* * De-register an ATM Endpoint Service * * Each ATM endpoint service provider must de-register its registered * endpoint(s) before terminating. Specifically, loaded kernel modules * must de-register their services before unloading themselves. * * Arguments: * epp pointer to endpoint definition structure * * Returns: * 0 de-registration successful * errno de-registration failed - reason indicated * */ int atm_endpoint_deregister(epp) Atm_endpoint *epp; { int s = splnet(); /* * Validate endpoint */ if (epp->ep_id > ENDPT_MAX) { (void) splx(s); return (EINVAL); } if (atm_endpoints[epp->ep_id] != epp) { (void) splx(s); return (ENOENT); } /* * Remove endpoint from list */ atm_endpoints[epp->ep_id] = NULL; (void) splx(s); return (0); }