diff options
Diffstat (limited to 'sys/netatm/atm_cm.c')
-rw-r--r-- | sys/netatm/atm_cm.c | 3464 |
1 files changed, 3464 insertions, 0 deletions
diff --git a/sys/netatm/atm_cm.c b/sys/netatm/atm_cm.c new file mode 100644 index 0000000..55612e3 --- /dev/null +++ b/sys/netatm/atm_cm.c @@ -0,0 +1,3464 @@ +/* + * + * =================================== + * 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. + * + * @(#) $Id: atm_cm.c,v 1.8 1998/08/06 18:10:42 mks Exp $ + * + */ + +/* + * Core ATM Services + * ----------------- + * + * ATM Connection Manager + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: atm_cm.c,v 1.8 1998/08/06 18:10:42 mks Exp $"; +#endif + +#include <netatm/kern_include.h> + + +/* + * Global variables + */ +struct atm_cm_stat atm_cm_stat = {0}; + +/* + * Local functions + */ +static void atm_cm_cpcs_upper __P((int, void *, int, int)); +static void atm_cm_saal_upper __P((int, void *, int, int)); +static void atm_cm_sscop_upper __P((int, void *, int, int)); +static Atm_connvc * atm_cm_share_llc __P((Atm_attributes *)); +static void atm_cm_closeconn __P((Atm_connection *, + struct t_atm_cause *)); +static void atm_cm_closevc __P((Atm_connvc *)); +static void atm_cm_timeout __P((struct atm_time *)); +static KTimeout_ret atm_cm_procinq __P((void *)); +static void atm_cm_incall __P((Atm_connvc *)); +static int atm_cm_accept __P((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)__P((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 = (Atm_attributes *)atm_allocate(&atm_attributes_pool); + 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) + atm_free((caddr_t)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 (KM_CMP(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) { + + 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 <sap>_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=0x%x cause=%d\n", + (int)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=0x%x, state=%d\n", + (int)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) { + 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)) { + + 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)__P((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 (KM_CMP(&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 (KM_CMP(&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 (KM_CMP(&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 (KM_CMP(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 (KM_CMP(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: + atm_free((caddr_t)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=0x%x, state=%d\n", + (int)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 *); + KM_COPY(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 (KM_CMP(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); +} + |