817 lines
16 KiB
C
817 lines
16 KiB
C
/*-
|
|
* ===================================
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* Core ATM Services
|
|
* -----------------
|
|
*
|
|
* ATM device support functions
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/syslog.h>
|
|
#include <net/if.h>
|
|
#include <net/bpf.h>
|
|
#include <netatm/port.h>
|
|
#include <netatm/queue.h>
|
|
#include <netatm/atm.h>
|
|
#include <netatm/atm_sys.h>
|
|
#include <netatm/atm_sap.h>
|
|
#include <netatm/atm_cm.h>
|
|
#include <netatm/atm_if.h>
|
|
#include <netatm/atm_vc.h>
|
|
#include <netatm/atm_stack.h>
|
|
#include <netatm/atm_pcb.h>
|
|
#include <netatm/atm_var.h>
|
|
|
|
/*
|
|
* Private structures for managing allocated kernel memory resources
|
|
*
|
|
* For each allocation of kernel memory, one Mem_ent will be used.
|
|
* The Mem_ent structures will be allocated in blocks inside of a
|
|
* Mem_blk structure.
|
|
*/
|
|
#define MEM_NMEMENT 10 /* How many Mem_ent's in a Mem_blk */
|
|
|
|
struct mem_ent {
|
|
void *me_kaddr; /* Allocated memory address */
|
|
u_int me_ksize; /* Allocated memory length */
|
|
void *me_uaddr; /* Memory address returned to caller */
|
|
u_int me_flags; /* Flags (see below) */
|
|
};
|
|
typedef struct mem_ent Mem_ent;
|
|
|
|
/*
|
|
* Memory entry flags
|
|
*/
|
|
#define MEF_NONCACHE 1 /* Memory is noncacheable */
|
|
|
|
|
|
struct mem_blk {
|
|
struct mem_blk *mb_next; /* Next block in chain */
|
|
Mem_ent mb_mement[MEM_NMEMENT]; /* Allocated memory entries */
|
|
};
|
|
typedef struct mem_blk Mem_blk;
|
|
|
|
static Mem_blk *atm_mem_head = NULL;
|
|
|
|
static struct t_atm_cause atm_dev_cause = {
|
|
T_ATM_ITU_CODING,
|
|
T_ATM_LOC_USER,
|
|
T_ATM_CAUSE_VPCI_VCI_ASSIGNMENT_FAILURE,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
extern struct ifqueue atm_intrq;
|
|
|
|
/*
|
|
* ATM Device Stack Instantiation
|
|
*
|
|
* Called at splnet.
|
|
*
|
|
* Arguments
|
|
* ssp pointer to array of stack definition pointers
|
|
* for connection
|
|
* ssp[0] points to upper layer's stack definition
|
|
* ssp[1] points to this layer's stack definition
|
|
* ssp[2] points to lower layer's stack definition
|
|
* cvcp pointer to connection vcc for this stack
|
|
*
|
|
* Returns
|
|
* 0 instantiation successful
|
|
* err instantiation failed - reason indicated
|
|
*
|
|
*/
|
|
int
|
|
atm_dev_inst(ssp, cvcp)
|
|
struct stack_defn **ssp;
|
|
Atm_connvc *cvcp;
|
|
{
|
|
Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
|
|
Cmn_vcc *cvp;
|
|
int err;
|
|
|
|
/*
|
|
* Check to see if device has been initialized
|
|
*/
|
|
if ((cup->cu_flags & CUF_INITED) == 0)
|
|
return ( EIO );
|
|
|
|
/*
|
|
* Validate lower SAP
|
|
*/
|
|
/*
|
|
* Device driver is the lowest layer - no need to validate
|
|
*/
|
|
|
|
/*
|
|
* Validate PVC vpi.vci
|
|
*/
|
|
if (cvcp->cvc_attr.called.addr.address_format == T_ATM_PVC_ADDR) {
|
|
/*
|
|
* Look through existing circuits - return error if found
|
|
*/
|
|
Atm_addr_pvc *pp;
|
|
|
|
pp = (Atm_addr_pvc *)cvcp->cvc_attr.called.addr.address;
|
|
if (atm_dev_vcc_find(cup, ATM_PVC_GET_VPI(pp),
|
|
ATM_PVC_GET_VCI(pp), 0))
|
|
return ( EADDRINUSE );
|
|
}
|
|
|
|
/*
|
|
* Validate our SAP type
|
|
*/
|
|
switch ((*(ssp+1))->sd_sap) {
|
|
case SAP_CPCS_AAL3_4:
|
|
case SAP_CPCS_AAL5:
|
|
case SAP_ATM:
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* Allocate a VCC control block
|
|
* This can happen from a callout so don't wait here.
|
|
*/
|
|
cvp = uma_zalloc(cup->cu_vcc_zone, M_NOWAIT);
|
|
if (cvp == NULL)
|
|
return (ENOMEM);
|
|
|
|
cvp->cv_state = CVS_INST;
|
|
cvp->cv_toku = (*ssp)->sd_toku;
|
|
cvp->cv_upper = (*ssp)->sd_upper;
|
|
cvp->cv_connvc = cvcp;
|
|
|
|
/*
|
|
* Let device have a look at the connection request
|
|
*/
|
|
err = (*cup->cu_instvcc)(cup, cvp);
|
|
if (err) {
|
|
uma_zfree(cup->cu_vcc_zone, cvp);
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* Looks good so far, so link in device VCC
|
|
*/
|
|
LINK2TAIL ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
|
|
|
|
/*
|
|
* Save my token
|
|
*/
|
|
(*++ssp)->sd_toku = cvp;
|
|
|
|
/*
|
|
* Pass instantiation down the stack
|
|
*/
|
|
/*
|
|
* No need - we're the lowest point.
|
|
*/
|
|
/* err = (*(ssp + 1))->sd_inst(ssp, cvcp); */
|
|
|
|
/*
|
|
* Save the lower layer's interface info
|
|
*/
|
|
/*
|
|
* No need - we're the lowest point
|
|
*/
|
|
/* cvp->cv_lower = (*++ssp)->sd_lower; */
|
|
/* cvp->cv_tok1 = (*ssp)->sd_toku; */
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* ATM Device Stack Command Handler
|
|
*
|
|
* Arguments
|
|
* cmd stack command code
|
|
* tok session token (Cmn_vcc)
|
|
* arg1 command specific argument
|
|
* arg2 command specific argument
|
|
*
|
|
* Returns
|
|
* none
|
|
*
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
atm_dev_lower(cmd, tok, arg1, arg2)
|
|
int cmd;
|
|
void *tok;
|
|
intptr_t arg1;
|
|
intptr_t arg2;
|
|
{
|
|
Cmn_vcc *cvp = (Cmn_vcc *)tok;
|
|
Atm_connvc *cvcp = cvp->cv_connvc;
|
|
Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
|
|
struct vccb *vcp;
|
|
u_int state;
|
|
int s;
|
|
|
|
switch ( cmd ) {
|
|
|
|
case CPCS_INIT:
|
|
/*
|
|
* Sanity check
|
|
*/
|
|
if ( cvp->cv_state != CVS_INST ) {
|
|
log ( LOG_ERR,
|
|
"atm_dev_lower: INIT: tok=%p, state=%d\n",
|
|
tok, cvp->cv_state );
|
|
break;
|
|
}
|
|
|
|
vcp = cvp->cv_connvc->cvc_vcc;
|
|
|
|
/*
|
|
* Validate SVC vpi.vci
|
|
*/
|
|
if ( vcp->vc_type & VCC_SVC ) {
|
|
|
|
if (atm_dev_vcc_find(cup, vcp->vc_vpi, vcp->vc_vci,
|
|
vcp->vc_type & (VCC_IN | VCC_OUT))
|
|
!= cvp){
|
|
log ( LOG_ERR,
|
|
"atm_dev_lower: dup SVC (%d,%d) tok=%p\n",
|
|
vcp->vc_vpi, vcp->vc_vci, tok );
|
|
atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Tell the device to open the VCC
|
|
*/
|
|
cvp->cv_state = CVS_INITED;
|
|
s = splimp();
|
|
if ((*cup->cu_openvcc)(cup, cvp)) {
|
|
atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
|
|
(void) splx(s);
|
|
break;
|
|
}
|
|
(void) splx(s);
|
|
break;
|
|
|
|
case CPCS_TERM: {
|
|
KBuffer *m, *prev, *next;
|
|
int *ip;
|
|
|
|
s = splimp();
|
|
|
|
/*
|
|
* Disconnect the VCC - ignore return code
|
|
*/
|
|
if ((cvp->cv_state == CVS_INITED) ||
|
|
(cvp->cv_state == CVS_ACTIVE)) {
|
|
(void) (*cup->cu_closevcc)(cup, cvp);
|
|
}
|
|
cvp->cv_state = CVS_TERM;
|
|
|
|
/*
|
|
* Remove from interface list
|
|
*/
|
|
UNLINK ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
|
|
|
|
/*
|
|
* Free any buffers from this VCC on the ATM interrupt queue
|
|
*/
|
|
prev = NULL;
|
|
IF_LOCK(&atm_intrq);
|
|
for (m = atm_intrq.ifq_head; m; m = next) {
|
|
next = KB_QNEXT(m);
|
|
|
|
/*
|
|
* See if this entry is for the terminating VCC
|
|
*/
|
|
KB_DATASTART(m, ip, int *);
|
|
ip++;
|
|
if (*ip == (intptr_t)cvp) {
|
|
/*
|
|
* Yep, so dequeue the entry
|
|
*/
|
|
if (prev == NULL)
|
|
atm_intrq.ifq_head = next;
|
|
else
|
|
KB_QNEXT(prev) = next;
|
|
|
|
if (next == NULL)
|
|
atm_intrq.ifq_tail = prev;
|
|
|
|
atm_intrq.ifq_len--;
|
|
|
|
/*
|
|
* Free the unwanted buffers
|
|
*/
|
|
KB_FREEALL(m);
|
|
} else {
|
|
prev = m;
|
|
}
|
|
}
|
|
IF_UNLOCK(&atm_intrq);
|
|
(void) splx(s);
|
|
|
|
/*
|
|
* Free VCC resources
|
|
*/
|
|
uma_zfree(cup->cu_vcc_zone, cvp);
|
|
break;
|
|
}
|
|
|
|
case CPCS_UNITDATA_INV:
|
|
|
|
/*
|
|
* Sanity check
|
|
*
|
|
* Use temp state variable since we dont want to lock out
|
|
* interrupts, but initial VC activation interrupt may
|
|
* happen here, changing state somewhere in the middle.
|
|
*/
|
|
state = cvp->cv_state;
|
|
if ((state != CVS_ACTIVE) &&
|
|
(state != CVS_INITED)) {
|
|
log ( LOG_ERR,
|
|
"atm_dev_lower: UNITDATA: tok=%p, state=%d\n",
|
|
tok, state );
|
|
KB_FREEALL((KBuffer *)arg1);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Send the packet to the interface's bpf if this vc has one.
|
|
*/
|
|
if (cvcp->cvc_vcc != NULL && cvcp->cvc_vcc->vc_nif != NULL) {
|
|
struct ifnet *ifp =
|
|
(struct ifnet *)cvcp->cvc_vcc->vc_nif;
|
|
|
|
BPF_MTAP(ifp, (KBuffer *)arg1);
|
|
}
|
|
|
|
/*
|
|
* Hand the data off to the device
|
|
*/
|
|
(*cup->cu_output)(cup, cvp, (KBuffer *)arg1);
|
|
|
|
break;
|
|
|
|
case CPCS_UABORT_INV:
|
|
log ( LOG_ERR,
|
|
"atm_dev_lower: unimplemented stack cmd 0x%x, tok=%p\n",
|
|
cmd, tok );
|
|
break;
|
|
|
|
default:
|
|
log ( LOG_ERR,
|
|
"atm_dev_lower: unknown stack cmd 0x%x, tok=%p\n",
|
|
cmd, tok );
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Allocate kernel memory block
|
|
*
|
|
* This function will allocate a kernel memory block of the type specified
|
|
* in the flags parameter. The returned address will point to a memory
|
|
* block of the requested size and alignment. The memory block will also
|
|
* be zeroed. The alloc/free functions will manage/mask both the OS-specific
|
|
* kernel memory management requirements and the bookkeeping required to
|
|
* deal with data alignment issues.
|
|
*
|
|
* This function should not be called from interrupt level.
|
|
*
|
|
* Arguments:
|
|
* size size of memory block to allocate
|
|
* align data alignment requirement
|
|
* flags allocation flags (ATM_DEV_*)
|
|
*
|
|
* Returns:
|
|
* uaddr pointer to aligned memory block
|
|
* NULL unable to allocate memory
|
|
*
|
|
*/
|
|
void *
|
|
atm_dev_alloc(size, align, flags)
|
|
u_int size;
|
|
u_int align;
|
|
u_int flags;
|
|
{
|
|
Mem_blk *mbp;
|
|
Mem_ent *mep;
|
|
u_int kalign, ksize;
|
|
int s, i;
|
|
|
|
s = splimp();
|
|
|
|
/*
|
|
* Find a free Mem_ent
|
|
*/
|
|
mep = NULL;
|
|
for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
|
|
for (i = 0; i < MEM_NMEMENT; i++) {
|
|
if (mbp->mb_mement[i].me_uaddr == NULL) {
|
|
mep = &mbp->mb_mement[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there are no free Mem_ent's, then allocate a new Mem_blk
|
|
* and link it into the chain
|
|
*/
|
|
if (mep == NULL) {
|
|
mbp = malloc(sizeof(Mem_blk), M_DEVBUF, M_NOWAIT|M_ZERO);
|
|
if (mbp == NULL) {
|
|
log(LOG_ERR, "atm_dev_alloc: Mem_blk failure\n");
|
|
(void) splx(s);
|
|
return (NULL);
|
|
}
|
|
mbp->mb_next = atm_mem_head;
|
|
atm_mem_head = mbp;
|
|
mep = mbp->mb_mement;
|
|
}
|
|
|
|
/*
|
|
* Now we need to get the kernel's allocation alignment minimum
|
|
*
|
|
* This is obviously very OS-specific stuff
|
|
*/
|
|
kalign = MINALLOCSIZE;
|
|
|
|
/*
|
|
* Figure out how much memory we must allocate to satify the
|
|
* user's size and alignment needs
|
|
*/
|
|
if (align <= kalign)
|
|
ksize = size;
|
|
else
|
|
ksize = size + align - kalign;
|
|
|
|
/*
|
|
* Finally, go get the memory
|
|
*/
|
|
if (flags & ATM_DEV_NONCACHE) {
|
|
mep->me_kaddr = malloc(ksize, M_DEVBUF, M_NOWAIT);
|
|
} else {
|
|
mep->me_kaddr = malloc(ksize, M_DEVBUF, M_NOWAIT);
|
|
}
|
|
|
|
if (mep->me_kaddr == NULL) {
|
|
log(LOG_ERR, "atm_dev_alloc: %skernel memory unavailable\n",
|
|
(flags & ATM_DEV_NONCACHE) ? "non-cacheable " : "");
|
|
(void) splx(s);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Calculate correct alignment address to pass back to user
|
|
*/
|
|
mep->me_uaddr = (void *) roundup((uintptr_t)mep->me_kaddr, align);
|
|
mep->me_ksize = ksize;
|
|
mep->me_flags = flags;
|
|
|
|
/*
|
|
* Clear memory for user
|
|
*/
|
|
bzero(mep->me_uaddr, size);
|
|
|
|
ATM_DEBUG4("atm_dev_alloc: size=%d, align=%d, flags=%d, uaddr=%p\n",
|
|
size, align, flags, mep->me_uaddr);
|
|
|
|
(void) splx(s);
|
|
|
|
return (mep->me_uaddr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Free kernel memory block
|
|
*
|
|
* This function will free a kernel memory block previously allocated by
|
|
* the atm_dev_alloc function.
|
|
*
|
|
* This function should not be called from interrupt level.
|
|
*
|
|
* Arguments:
|
|
* uaddr pointer to allocated aligned memory block
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
atm_dev_free(uaddr)
|
|
volatile void *uaddr;
|
|
{
|
|
Mem_blk *mbp;
|
|
Mem_ent *mep;
|
|
int s, i;
|
|
|
|
ATM_DEBUG1("atm_dev_free: uaddr=%p\n", uaddr);
|
|
|
|
s = splimp();
|
|
|
|
/*
|
|
* Protect ourselves...
|
|
*/
|
|
if (uaddr == NULL)
|
|
panic("atm_dev_free: trying to free null address");
|
|
|
|
/*
|
|
* Find our associated entry
|
|
*/
|
|
mep = NULL;
|
|
for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
|
|
for (i = 0; i < MEM_NMEMENT; i++) {
|
|
if (mbp->mb_mement[i].me_uaddr == uaddr) {
|
|
mep = &mbp->mb_mement[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we didn't find our entry, then unceremoniously let the caller
|
|
* know they screwed up (it certainly couldn't be a bug here...)
|
|
*/
|
|
if (mep == NULL)
|
|
panic("atm_dev_free: trying to free unknown address");
|
|
|
|
/*
|
|
* Give the memory space back to the kernel
|
|
*/
|
|
if (mep->me_flags & ATM_DEV_NONCACHE) {
|
|
free(mep->me_kaddr, M_DEVBUF);
|
|
} else {
|
|
free(mep->me_kaddr, M_DEVBUF);
|
|
}
|
|
|
|
/*
|
|
* Free our entry
|
|
*/
|
|
mep->me_uaddr = NULL;
|
|
|
|
(void) splx(s);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Compress buffer chain
|
|
*
|
|
* This function will compress a supplied buffer chain into a minimum number
|
|
* of kernel buffers. Typically, this function will be used because the
|
|
* number of buffers in an output buffer chain is too large for a device's
|
|
* DMA capabilities. This should only be called as a last resort, since
|
|
* all the data copying will surely kill any hopes of decent performance.
|
|
*
|
|
* Arguments:
|
|
* m pointer to source buffer chain
|
|
*
|
|
* Returns:
|
|
* n pointer to compressed buffer chain
|
|
*
|
|
*/
|
|
KBuffer *
|
|
atm_dev_compress(m)
|
|
KBuffer *m;
|
|
{
|
|
KBuffer *n, *n0, **np;
|
|
int len, space;
|
|
caddr_t src, dst;
|
|
|
|
n = n0 = NULL;
|
|
np = &n0;
|
|
dst = NULL;
|
|
space = 0;
|
|
|
|
/*
|
|
* Copy each source buffer into compressed chain
|
|
*/
|
|
while (m) {
|
|
|
|
if (space == 0) {
|
|
|
|
/*
|
|
* Allocate another buffer for compressed chain
|
|
*/
|
|
KB_ALLOCEXT(n, ATM_DEV_CMPR_LG, KB_F_NOWAIT, KB_T_DATA);
|
|
if (n) {
|
|
space = ATM_DEV_CMPR_LG;
|
|
} else {
|
|
KB_ALLOC(n, ATM_DEV_CMPR_SM, KB_F_NOWAIT,
|
|
KB_T_DATA);
|
|
if (n) {
|
|
space = ATM_DEV_CMPR_SM;
|
|
} else {
|
|
/*
|
|
* Unable to get any new buffers, so
|
|
* just return the partially compressed
|
|
* chain and hope...
|
|
*/
|
|
*np = m;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KB_HEADSET(n, 0);
|
|
KB_LEN(n) = 0;
|
|
KB_BFRSTART(n, dst, caddr_t);
|
|
|
|
*np = n;
|
|
np = &KB_NEXT(n);
|
|
}
|
|
|
|
/*
|
|
* Copy what we can from source buffer
|
|
*/
|
|
len = MIN(space, KB_LEN(m));
|
|
KB_DATASTART(m, src, caddr_t);
|
|
bcopy(src, dst, len);
|
|
|
|
/*
|
|
* Adjust for copied data
|
|
*/
|
|
dst += len;
|
|
space -= len;
|
|
|
|
KB_HEADADJ(m, -len);
|
|
KB_TAILADJ(n, len);
|
|
|
|
/*
|
|
* If we've exhausted our current source buffer, free it
|
|
* and move to the next one
|
|
*/
|
|
if (KB_LEN(m) == 0) {
|
|
KB_FREEONE(m, m);
|
|
}
|
|
}
|
|
|
|
return (n0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Locate VCC entry
|
|
*
|
|
* This function will return the VCC entry for a specified interface and
|
|
* VPI/VCI value.
|
|
*
|
|
* Arguments:
|
|
* cup pointer to interface unit structure
|
|
* vpi VPI value
|
|
* vci VCI value
|
|
* type VCC type
|
|
*
|
|
* Returns:
|
|
* vcp pointer to located VCC entry matching
|
|
* NULL no VCC found
|
|
*
|
|
*/
|
|
Cmn_vcc *
|
|
atm_dev_vcc_find(cup, vpi, vci, type)
|
|
Cmn_unit *cup;
|
|
u_int vpi;
|
|
u_int vci;
|
|
u_int type;
|
|
{
|
|
Cmn_vcc *cvp;
|
|
int s = splnet();
|
|
|
|
/*
|
|
* Go find VCC
|
|
*
|
|
* (Probably should stick in a hash table some time)
|
|
*/
|
|
for (cvp = cup->cu_vcc; cvp; cvp = cvp->cv_next) {
|
|
struct vccb *vcp;
|
|
|
|
vcp = cvp->cv_connvc->cvc_vcc;
|
|
if ((vcp->vc_vci == vci) && (vcp->vc_vpi == vpi) &&
|
|
((vcp->vc_type & type) == type))
|
|
break;
|
|
}
|
|
|
|
(void) splx(s);
|
|
return (cvp);
|
|
}
|
|
|
|
|
|
#ifdef notdef
|
|
/*
|
|
* Module unloading notification
|
|
*
|
|
* This function must be called just prior to unloading the module from
|
|
* memory. All allocated memory will be freed here and anything else that
|
|
* needs cleaning up.
|
|
*
|
|
* Arguments:
|
|
* none
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
atm_unload()
|
|
{
|
|
Mem_blk *mbp;
|
|
Mem_ent *mep;
|
|
int s, i;
|
|
|
|
s = splimp();
|
|
|
|
/*
|
|
* Free up all of our memory management storage
|
|
*/
|
|
while (mbp = atm_mem_head) {
|
|
|
|
/*
|
|
* Make sure users have freed up all of their memory
|
|
*/
|
|
for (i = 0; i < MEM_NMEMENT; i++) {
|
|
if (mbp->mb_mement[i].me_uaddr != NULL) {
|
|
panic("atm_unload: unfreed memory");
|
|
}
|
|
}
|
|
|
|
atm_mem_head = mbp->mb_next;
|
|
|
|
/*
|
|
* Hand this block back to the kernel
|
|
*/
|
|
free((caddr_t)mbp, M_DEVBUF);
|
|
}
|
|
|
|
(void) splx(s);
|
|
|
|
return;
|
|
}
|
|
#endif /* notdef */
|
|
|
|
|
|
/*
|
|
* Print a PDU
|
|
*
|
|
* Arguments:
|
|
* cup pointer to device unit
|
|
* cvp pointer to VCC control block
|
|
* m pointer to pdu buffer chain
|
|
* msg pointer to message string
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
atm_dev_pdu_print(const Cmn_unit *cup, const Cmn_vcc *cvp,
|
|
const KBuffer *m, const char *msg)
|
|
{
|
|
char buf[128];
|
|
|
|
snprintf(buf, sizeof(buf), "%s vcc=(%d,%d)", msg,
|
|
cvp->cv_connvc->cvc_vcc->vc_vpi,
|
|
cvp->cv_connvc->cvc_vcc->vc_vci);
|
|
|
|
atm_pdu_print(m, buf);
|
|
}
|