/* * * =================================== * 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$ * */ /* * PVC-only Signalling Manager * --------------------------- * * External interfaces to SigPVC manager. Includes support for * running as a loadable kernel module. * */ #ifndef ATM_SIGPVC_MODULE #include "opt_atm.h" #endif #include #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif /* * Global variables */ struct sp_info sigpvc_vcpool = { "sigpvc vcc pool", /* si_name */ sizeof(struct sigpvc_vccb), /* si_blksiz */ 10, /* si_blkcnt */ 50 /* si_maxallow */ }; /* * Local functions */ static int sigpvc_start __P((void)); static int sigpvc_stop __P((void)); static int sigpvc_attach __P((struct sigmgr *, struct atm_pif *)); static int sigpvc_detach __P((struct atm_pif *)); static int sigpvc_setup __P((Atm_connvc *, int *)); static int sigpvc_release __P((struct vccb *, int *)); static int sigpvc_free __P((struct vccb *)); static int sigpvc_ioctl __P((int, caddr_t, caddr_t)); /* * Local variables */ static int sigpvc_registered = 0; static struct sigmgr sigpvc_mgr = { NULL, ATM_SIG_PVC, NULL, sigpvc_attach, sigpvc_detach, sigpvc_setup, NULL, NULL, sigpvc_release, sigpvc_free, sigpvc_ioctl }; static struct attr_cause sigpvc_cause = { T_ATM_PRESENT, { T_ATM_ITU_CODING, T_ATM_LOC_USER, T_ATM_CAUSE_UNSPECIFIED_NORMAL, {0, 0, 0, 0} } }; /* * Initialize sigpvc processing * * This will be called during module loading. We'll just register * the sigpvc protocol descriptor and wait for a SigPVC ATM interface * to come online. * * Arguments: * none * * Returns: * 0 startup was successful * errno startup failed - reason indicated * */ static int sigpvc_start() { int err = 0; /* * Verify software version */ if (atm_version != ATM_VERSION) { log(LOG_ERR, "version mismatch: sigpvc=%d.%d kernel=%d.%d\n", ATM_VERS_MAJ(ATM_VERSION), ATM_VERS_MIN(ATM_VERSION), ATM_VERS_MAJ(atm_version), ATM_VERS_MIN(atm_version)); return (EINVAL); } /* * Register ourselves with system */ err = atm_sigmgr_register(&sigpvc_mgr); if (err == 0) sigpvc_registered = 1; return (err); } /* * Halt sigpvc processing * * This should be called just prior to unloading the module from * memory. All sigpvc interfaces must be deregistered before the * protocol can be shutdown. * * Arguments: * none * * Returns: * 0 shutdown was successful * errno shutdown failed - reason indicated * */ static int sigpvc_stop() { int err = 0; int s = splnet(); /* * Is protocol even setup? */ if (sigpvc_registered) { /* * Any protocol instances still registered?? */ if (sigpvc_mgr.sm_prinst) { /* Yes, can't stop now */ err = EBUSY; goto done; } /* * De-register from system */ err = atm_sigmgr_deregister(&sigpvc_mgr); sigpvc_registered = 0; /* * Free up our vccb storage pool */ atm_release_pool(&sigpvc_vcpool); } else err = ENXIO; done: (void) splx(s); return (err); } /* * Attach a SigPVC-controlled interface * * Each ATM physical interface must be attached with the signalling manager for * the interface's signalling protocol (via the atm_sigmgr_attach function). * This function will handle the attachment for SigPVC-controlled interfaces. * A new sigpvc protocol instance will be created and then we'll just sit * around waiting for connection requests. * * Function must be called at splnet. * * Arguments: * smp pointer to sigpvc signalling manager control block * pip pointer to atm physical interface control block * * Returns: * 0 attach successful * errno attach failed - reason indicated * */ static int sigpvc_attach(smp, pip) struct sigmgr *smp; struct atm_pif *pip; { int err = 0; struct sigpvc *pvp = NULL; /* * Allocate sigpvc protocol instance control block */ pvp = (struct sigpvc *) KM_ALLOC(sizeof(struct sigpvc), M_DEVBUF, M_NOWAIT); if (pvp == NULL) { err = ENOMEM; goto done; } KM_ZERO(pvp, sizeof(struct sigpvc)); /* * Link instance into manager's chain */ LINK2TAIL((struct siginst *)pvp, struct siginst, smp->sm_prinst, si_next); /* * Finally, set state and link in interface */ pvp->pv_pif = pip; pvp->pv_state = SIGPVC_ACTIVE; pip->pif_sigmgr = smp; pip->pif_siginst = (struct siginst *)pvp; done: /* * Reset our work if attach fails */ if (err) { pip->pif_sigmgr = NULL; pip->pif_siginst = NULL; if (pvp) { UNLINK((struct siginst *)pvp, struct siginst, smp->sm_prinst, si_next); KM_FREE(pvp, sizeof(struct sigpvc), M_DEVBUF); } } return (err); } /* * Detach a SigPVC-controlled interface * * Each ATM physical interface may be detached from its signalling manager * (via the atm_sigmgr_detach function). This function will handle the * detachment for all SigPVC-controlled interfaces. All circuits will be * immediately terminated. * * Function must be called at splnet. * * Arguments: * pip pointer to atm physical interface control block * * Returns: * 0 detach successful * errno detach failed - reason indicated * */ static int sigpvc_detach(pip) struct atm_pif *pip; { struct sigpvc *pvp; struct vccb *vcp, *vnext; /* * Get SigPVC protocol instance */ pvp = (struct sigpvc *)pip->pif_siginst; /* * Terminate all of our VCCs */ for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; vcp = vnext){ u_char oustate; vnext = Q_NEXT(vcp, struct vccb, vc_sigelem); /* * Close VCC and notify owner */ oustate = vcp->vc_ustate; sigpvc_close_vcc(vcp); if (oustate == VCCU_OPEN) { vcp->vc_connvc->cvc_attr.cause = sigpvc_cause; atm_cm_cleared(vcp->vc_connvc); } } /* * If there are no vcc's queued, then get rid of the protocol * instance. */ if (Q_HEAD(pvp->pv_vccq, struct vccb) == NULL) { struct sigmgr *smp = pip->pif_sigmgr; pip->pif_sigmgr = NULL; pip->pif_siginst = NULL; UNLINK((struct siginst *)pvp, struct siginst, smp->sm_prinst, si_next); KM_FREE(pvp, sizeof(struct sigpvc), M_DEVBUF); } else { /* * Otherwise, set new state indicating detach in progress. * The protocol instance will be freed during sigpvc_free * processing for the last queued vcc. */ pvp->pv_state = SIGPVC_DETACH; } return (0); } /* * Open a SigPVC ATM Connection * * All service user requests to open a VC connection (via atm_open_connection) * over an ATM interface attached to the SigPVC signalling manager are handled * here. Only PVC requests are allowed. * * Function will be called at splnet. * * Arguments: * cvp pointer to CM's connection VCC * errp location to store an error code if CALL_FAILED is returned * * Returns: * CALL_PROCEEDING - connection establishment is in progress * CALL_FAILED - connection establishment failed * CALL_CONNECTED - connection has been successfully established * */ static int sigpvc_setup(cvp, errp) Atm_connvc *cvp; int *errp; { struct sigpvc *pvp = (struct sigpvc *)cvp->cvc_attr.nif->nif_pif->pif_siginst; int ret; /* * See what signalling has to say */ switch (pvp->pv_state) { case SIGPVC_ACTIVE: break; default: *errp = ENXIO; ret = CALL_FAILED; goto done; } /* * Open requested type of connection */ switch (cvp->cvc_attr.called.addr.address_format) { case T_ATM_PVC_ADDR: /* * Create a PVC */ ret = sigpvc_create_pvc(pvp, cvp, errp); break; default: *errp = EPROTONOSUPPORT; ret = CALL_FAILED; } done: return (ret); } /* * Close a SigPVC ATM Connection * * All service user requests to terminate a previously open VC connection * (via the atm_close_connection function), which is running over an interface * attached to the SigPVC signalling manager, are handled here. * * Function will be called at splnet. * * Arguments: * vcp pointer to connection's VC control block * errp location to store an error code if CALL_FAILED is returned * * Returns: * CALL_PROCEEDING - connection termination is in progress * CALL_FAILED - connection termination failed * CALL_CLEARED - connection has been successfully terminated * */ static int sigpvc_release(vcp, errp) struct vccb *vcp; int *errp; { /* * Make sure VCC is open */ if ((vcp->vc_sstate == VCCS_NULL) || (vcp->vc_sstate == VCCS_FREE) || (vcp->vc_ustate == VCCU_NULL) || (vcp->vc_ustate == VCCU_CLOSED)) { *errp = EALREADY; return (CALL_FAILED); } /* * Not much else to do except close the vccb */ sigpvc_close_vcc(vcp); return (CALL_CLEARED); } /* * Free SigPVC ATM Connection Resources * * All service user requests to free the resources of a closed VCC connection * (via the atm_free_connection function), which is running over an interface * attached to the SigPVC signalling manager, are handled here. * * Function will be called at splnet. * * Arguments: * vcp pointer to connection's VCC control block * * Returns: * 0 connection free was successful * errno connection free failed - reason indicated * */ static int sigpvc_free(vcp) struct vccb *vcp; { struct atm_pif *pip = vcp->vc_pif; struct sigpvc *pvp = (struct sigpvc *)pip->pif_siginst; /* * Make sure VCC has been closed */ if ((vcp->vc_ustate != VCCU_CLOSED) || (vcp->vc_sstate != VCCS_FREE)) return (EEXIST); /* * Remove vccb from protocol queue */ DEQUEUE(vcp, struct vccb, vc_sigelem, pvp->pv_vccq); /* * Free vccb storage */ vcp->vc_ustate = VCCU_NULL; vcp->vc_sstate = VCCS_NULL; atm_free((caddr_t)vcp); /* * If we're detaching and this was the last vcc queued, * get rid of the protocol instance */ if ((pvp->pv_state == SIGPVC_DETACH) && (Q_HEAD(pvp->pv_vccq, struct vccb) == NULL)) { struct sigmgr *smp = pip->pif_sigmgr; pip->pif_sigmgr = NULL; pip->pif_siginst = NULL; UNLINK((struct siginst *)pvp, struct siginst, smp->sm_prinst, si_next); KM_FREE(pvp, sizeof(struct sigpvc), M_DEVBUF); } return (0); } /* * Process Signalling Manager PF_ATM ioctls * * Function will be called at splnet. * * Arguments: * code PF_ATM sub-operation code * data pointer to code specific parameter data area * arg1 pointer to code specific argument * * Returns: * 0 request procesed * errno error processing request - reason indicated * */ static int sigpvc_ioctl(code, data, arg1) int code; caddr_t data; caddr_t arg1; { struct atmdelreq *adp; struct atminfreq *aip; struct air_vcc_rsp avr; struct sigpvc *pvp; struct vccb *vcp; Atm_connection *cop; caddr_t cp; u_int vpi, vci; int i, space, err = 0; switch (code) { case AIOCS_DEL_PVC: /* * Delete a PVC */ adp = (struct atmdelreq *)data; pvp = (struct sigpvc *)arg1; /* * Find requested VCC */ vpi = adp->adr_pvc_vpi; vci = adp->adr_pvc_vci; for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; vcp = Q_NEXT(vcp, struct vccb, vc_sigelem)) { if ((vcp->vc_vpi == vpi) && (vcp->vc_vci == vci)) break; } if (vcp == NULL) return (ENOENT); /* * Schedule VCC termination */ err = atm_cm_abort(vcp->vc_connvc, &sigpvc_cause.v); break; case AIOCS_DEL_SVC: /* * Delete a SVC */ err = ENOENT; break; case AIOCS_INF_VCC: /* * Get VCC information */ aip = (struct atminfreq *)data; pvp = (struct sigpvc *)arg1; cp = aip->air_buf_addr; space = aip->air_buf_len; /* * Get info for all VCCs on interface */ for (vcp = Q_HEAD(pvp->pv_vccq, struct vccb); vcp; vcp = Q_NEXT(vcp, struct vccb, vc_sigelem)) { /* * Make sure there's room in user buffer */ if (space < sizeof(avr)) { err = ENOSPC; break; } /* * Fill in info to be returned */ (void) snprintf(avr.avp_intf, sizeof(avr.avp_intf), "%s%d", pvp->pv_pif->pif_name, pvp->pv_pif->pif_unit); avr.avp_vpi = vcp->vc_vpi; avr.avp_vci = vcp->vc_vci; avr.avp_type = vcp->vc_type; avr.avp_sig_proto = ATM_SIG_PVC; avr.avp_aal = vcp->vc_connvc->cvc_attr.aal.type; cop = vcp->vc_connvc->cvc_conn; if (cop) avr.avp_encaps = cop->co_mpx; else avr.avp_encaps = 0; KM_ZERO(avr.avp_owners, sizeof(avr.avp_owners)); for (i = 0; cop && i < sizeof(avr.avp_owners); cop = cop->co_next, i += T_ATM_APP_NAME_LEN+1) { strncpy(&avr.avp_owners[i], cop->co_endpt->ep_getname(cop->co_toku), T_ATM_APP_NAME_LEN); } avr.avp_state = vcp->vc_sstate; avr.avp_daddr.address_format = T_ATM_ABSENT; avr.avp_dsubaddr.address_format = T_ATM_ABSENT; avr.avp_ipdus = vcp->vc_ipdus; avr.avp_opdus = vcp->vc_opdus; avr.avp_ibytes = vcp->vc_ibytes; avr.avp_obytes = vcp->vc_obytes; avr.avp_ierrors = vcp->vc_ierrors; avr.avp_oerrors = vcp->vc_oerrors; avr.avp_tstamp = vcp->vc_tstamp; /* * Copy data to user buffer and update buffer info */ if ((err = copyout((caddr_t)&avr, cp, sizeof(avr))) != 0) break; cp += sizeof(avr); space -= sizeof(avr); } /* * Update buffer pointer/count */ aip->air_buf_addr = cp; aip->air_buf_len = space; break; case AIOCS_INF_ARP: case AIOCS_INF_ASV: /* * Get ARP table/server information */ /* We don't maintain any ARP information */ break; default: err = EOPNOTSUPP; } return (err); } #ifdef ATM_SIGPVC_MODULE /* ******************************************************************* * * Loadable Module Support * ******************************************************************* */ static int sigpvc_doload __P((void)); static int sigpvc_dounload __P((void)); /* * Generic module load processing * * This function is called by an OS-specific function when this * module is being loaded. * * Arguments: * none * * Returns: * 0 load was successful * errno load failed - reason indicated * */ static int sigpvc_doload() { int err = 0; /* * Start us up */ err = sigpvc_start(); if (err) /* Problems, clean up */ (void)sigpvc_stop(); return (err); } /* * Generic module unload processing * * This function is called by an OS-specific function when this * module is being unloaded. * * Arguments: * none * * Returns: * 0 unload was successful * errno unload failed - reason indicated * */ static int sigpvc_dounload() { int err = 0; /* * OK, try to clean up our mess */ err = sigpvc_stop(); return (err); } #ifdef sun /* * Loadable driver description */ struct vdldrv sigpvc_drv = { VDMAGIC_PSEUDO, /* Pseudo Driver */ "sigpvc_mod", /* name */ NULL, /* dev_ops */ NULL, /* bdevsw */ NULL, /* cdevsw */ 0, /* blockmajor */ 0 /* charmajor */ }; /* * Loadable module support entry point * * This is the routine called by the vd driver for all loadable module * functions for this pseudo driver. This routine name must be specified * on the modload(1) command. This routine will be called whenever the * modload(1), modunload(1) or modstat(1) commands are issued for this * module. * * Arguments: * cmd vd command code * vdp pointer to vd driver's structure * vdi pointer to command-specific vdioctl_* structure * vds pointer to status structure (VDSTAT only) * * Returns: * 0 command was successful * errno command failed - reason indicated * */ int sigpvc_mod(cmd, vdp, vdi, vds) int cmd; struct vddrv *vdp; caddr_t vdi; struct vdstat *vds; { int err = 0; switch (cmd) { case VDLOAD: /* * Module Load * * We dont support any user configuration */ err = sigpvc_doload(); if (err == 0) /* Let vd driver know about us */ vdp->vdd_vdtab = (struct vdlinkage *)&sigpvc_drv; break; case VDUNLOAD: /* * Module Unload */ err = sigpvc_dounload(); break; case VDSTAT: /* * Module Status */ /* Not much to say at the moment */ break; default: log(LOG_ERR, "sigpvc_mod: Unknown vd command 0x%x\n", cmd); err = EINVAL; } return (err); } #endif /* sun */ #ifdef __FreeBSD__ #include #include #include /* * Loadable miscellaneous module description */ MOD_MISC(sigpvc); /* * Loadable module support "load" entry point * * This is the routine called by the lkm driver whenever the * modload(1) command is issued for this module. * * Arguments: * lkmtp pointer to lkm drivers's structure * cmd lkm command code * * Returns: * 0 command was successful * errno command failed - reason indicated * */ static int sigpvc_load(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { return(sigpvc_doload()); } /* * Loadable module support "unload" entry point * * This is the routine called by the lkm driver whenever the * modunload(1) command is issued for this module. * * Arguments: * lkmtp pointer to lkm drivers's structure * cmd lkm command code * * Returns: * 0 command was successful * errno command failed - reason indicated * */ static int sigpvc_unload(lkmtp, cmd) struct lkm_table *lkmtp; int cmd; { return(sigpvc_dounload()); } /* * Loadable module support entry point * * This is the routine called by the lkm driver for all loadable module * functions for this driver. This routine name must be specified * on the modload(1) command. This routine will be called whenever the * modload(1), modunload(1) or modstat(1) commands are issued for this * module. * * Arguments: * lkmtp pointer to lkm drivers's structure * cmd lkm command code * ver lkm version * * Returns: * 0 command was successful * errno command failed - reason indicated * */ int sigpvc_mod(lkmtp, cmd, ver) struct lkm_table *lkmtp; int cmd; int ver; { MOD_DISPATCH(sigpvc, lkmtp, cmd, ver, sigpvc_load, sigpvc_unload, lkm_nullcmd); } #endif /* __FreeBSD__ */ #else /* !ATM_SIGPVC_MODULE */ /* ******************************************************************* * * Kernel Compiled Module Support * ******************************************************************* */ static void sigpvc_doload __P((void *)); SYSINIT(atmsigpvc, SI_SUB_PROTO_END, SI_ORDER_ANY, sigpvc_doload, NULL) /* * Kernel initialization * * Arguments: * arg Not used * * Returns: * none * */ static void sigpvc_doload(void *arg) { int err = 0; /* * Start us up */ err = sigpvc_start(); if (err) { /* Problems, clean up */ (void)sigpvc_stop(); log(LOG_ERR, "ATM SIGPVC unable to initialize (%d)!!\n", err); } return; } #endif /* ATM_SIGPVC_MODULE */