MFC:
[dragonfly.git] / sys / netproto / atm / atm_device.c
blob5753ba4be6711fcbdf3c7829aae911072811c8a6
1 /*
3 * ===================================
4 * HARP | Host ATM Research Platform
5 * ===================================
8 * This Host ATM Research Platform ("HARP") file (the "Software") is
9 * made available by Network Computing Services, Inc. ("NetworkCS")
10 * "AS IS". NetworkCS does not provide maintenance, improvements or
11 * support of any kind.
13 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17 * In no event shall NetworkCS be responsible for any damages, including
18 * but not limited to consequential damages, arising from or relating to
19 * any use of the Software or related support.
21 * Copyright 1994-1998 Network Computing Services, Inc.
23 * Copies of this Software may be made, however, the above copyright
24 * notice must be reproduced on all copies.
26 * @(#) $FreeBSD: src/sys/netatm/atm_device.c,v 1.5 1999/08/28 00:48:35 peter Exp $
27 * @(#) $DragonFly: src/sys/netproto/atm/atm_device.c,v 1.9 2006/12/20 18:14:43 dillon Exp $
31 * Core ATM Services
32 * -----------------
34 * ATM device support functions
38 #include "kern_include.h"
41 * Private structures for managing allocated kernel memory resources
43 * For each allocation of kernel memory, one Mem_ent will be used.
44 * The Mem_ent structures will be allocated in blocks inside of a
45 * Mem_blk structure.
47 #define MEM_NMEMENT 10 /* How many Mem_ent's in a Mem_blk */
49 struct mem_ent {
50 void *me_kaddr; /* Allocated memory address */
51 u_int me_ksize; /* Allocated memory length */
52 void *me_uaddr; /* Memory address returned to caller */
53 u_int me_flags; /* Flags (see below) */
55 typedef struct mem_ent Mem_ent;
58 * Memory entry flags
60 #define MEF_NONCACHE 1 /* Memory is noncacheable */
63 struct mem_blk {
64 struct mem_blk *mb_next; /* Next block in chain */
65 Mem_ent mb_mement[MEM_NMEMENT]; /* Allocated memory entries */
67 typedef struct mem_blk Mem_blk;
69 static Mem_blk *atm_mem_head = NULL;
71 static struct t_atm_cause atm_dev_cause = {
72 T_ATM_ITU_CODING,
73 T_ATM_LOC_USER,
74 T_ATM_CAUSE_VPCI_VCI_ASSIGNMENT_FAILURE,
75 {0, 0, 0, 0}
80 * ATM Device Stack Instantiation
82 * Called from a critical section.
84 * Arguments
85 * ssp pointer to array of stack definition pointers
86 * for connection
87 * ssp[0] points to upper layer's stack definition
88 * ssp[1] points to this layer's stack definition
89 * ssp[2] points to lower layer's stack definition
90 * cvcp pointer to connection vcc for this stack
92 * Returns
93 * 0 instantiation successful
94 * err instantiation failed - reason indicated
97 int
98 atm_dev_inst(struct stack_defn **ssp, Atm_connvc *cvcp)
100 Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
101 Cmn_vcc *cvp;
102 int err;
105 * Check to see if device has been initialized
107 if ((cup->cu_flags & CUF_INITED) == 0)
108 return ( EIO );
111 * Validate lower SAP
114 * Device driver is the lowest layer - no need to validate
118 * Validate PVC vpi.vci
120 if (cvcp->cvc_attr.called.addr.address_format == T_ATM_PVC_ADDR) {
122 * Look through existing circuits - return error if found
124 Atm_addr_pvc *pp;
126 pp = (Atm_addr_pvc *)cvcp->cvc_attr.called.addr.address;
127 if (atm_dev_vcc_find(cup, ATM_PVC_GET_VPI(pp),
128 ATM_PVC_GET_VCI(pp), 0))
129 return ( EADDRINUSE );
133 * Validate our SAP type
135 switch ((*(ssp+1))->sd_sap) {
136 case SAP_CPCS_AAL3_4:
137 case SAP_CPCS_AAL5:
138 case SAP_ATM:
139 break;
140 default:
141 return (EINVAL);
145 * Allocate a VCC control block
147 if ( ( cvp = (Cmn_vcc *)atm_allocate(cup->cu_vcc_pool) ) == NULL )
148 return ( ENOMEM );
150 cvp->cv_state = CVS_INST;
151 cvp->cv_toku = (*ssp)->sd_toku;
152 cvp->cv_upper = (*ssp)->sd_upper;
153 cvp->cv_connvc = cvcp;
156 * Let device have a look at the connection request
158 err = (*cup->cu_instvcc)(cup, cvp);
159 if (err) {
160 atm_free((caddr_t)cvp);
161 return (err);
165 * Looks good so far, so link in device VCC
167 LINK2TAIL ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
170 * Save my token
172 (*++ssp)->sd_toku = cvp;
175 * Pass instantiation down the stack
178 * No need - we're the lowest point.
180 /* err = (*(ssp + 1))->sd_inst(ssp, cvcp); */
183 * Save the lower layer's interface info
186 * No need - we're the lowest point
188 /* cvp->cv_lower = (*++ssp)->sd_lower; */
189 /* cvp->cv_tok1 = (*ssp)->sd_toku; */
191 return (0);
196 * ATM Device Stack Command Handler
198 * Arguments
199 * cmd stack command code
200 * tok session token (Cmn_vcc)
201 * arg1 command specific argument
202 * arg2 command specific argument
204 * Returns
205 * none
208 /*ARGSUSED*/
209 void
210 atm_dev_lower(int cmd, void *tok, int arg1, int arg2)
212 Cmn_vcc *cvp = (Cmn_vcc *)tok;
213 Atm_connvc *cvcp = cvp->cv_connvc;
214 Cmn_unit *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
215 struct vccb *vcp;
216 u_int state;
218 switch ( cmd ) {
220 case CPCS_INIT:
222 * Sanity check
224 if ( cvp->cv_state != CVS_INST ) {
225 log ( LOG_ERR,
226 "atm_dev_lower: INIT: tok=%p, state=%d\n",
227 tok, cvp->cv_state );
228 break;
231 vcp = cvp->cv_connvc->cvc_vcc;
234 * Validate SVC vpi.vci
236 if ( vcp->vc_type & VCC_SVC ) {
238 if (atm_dev_vcc_find(cup, vcp->vc_vpi, vcp->vc_vci,
239 vcp->vc_type & (VCC_IN | VCC_OUT))
240 != cvp){
241 log ( LOG_ERR,
242 "atm_dev_lower: dup SVC (%d,%d) tok=%p\n",
243 vcp->vc_vpi, vcp->vc_vci, tok );
244 atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
245 break;
250 * Tell the device to open the VCC
252 cvp->cv_state = CVS_INITED;
253 crit_enter();
254 if ((*cup->cu_openvcc)(cup, cvp)) {
255 atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
256 crit_exit();
257 break;
259 crit_exit();
260 break;
262 case CPCS_TERM: {
263 KBuffer *m, *prev, *next;
264 int *ip;
266 crit_enter();
269 * Disconnect the VCC - ignore return code
271 if ((cvp->cv_state == CVS_INITED) ||
272 (cvp->cv_state == CVS_ACTIVE)) {
273 (*cup->cu_closevcc)(cup, cvp);
275 cvp->cv_state = CVS_TERM;
278 * Remove from interface list
280 UNLINK ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
283 * Free any buffers from this VCC on the ATM interrupt queue
285 prev = NULL;
286 for (m = atm_intrq.ifq_head; m; m = next) {
287 next = KB_QNEXT(m);
290 * See if this entry is for the terminating VCC
292 KB_DATASTART(m, ip, int *);
293 ip++;
294 if (*ip == (int)cvp) {
296 * Yep, so dequeue the entry
298 if (prev == NULL)
299 atm_intrq.ifq_head = next;
300 else
301 KB_QNEXT(prev) = next;
303 if (next == NULL)
304 atm_intrq.ifq_tail = prev;
306 atm_intrq.ifq_len--;
309 * Free the unwanted buffers
311 KB_FREEALL(m);
312 } else {
313 prev = m;
316 crit_exit();
319 * Free VCC resources
321 atm_free((caddr_t)cvp);
322 break;
325 case CPCS_UNITDATA_INV:
328 * Sanity check
330 * Use temp state variable since we dont want to lock out
331 * interrupts, but initial VC activation interrupt may
332 * happen here, changing state somewhere in the middle.
334 state = cvp->cv_state;
335 if ((state != CVS_ACTIVE) &&
336 (state != CVS_INITED)) {
337 log ( LOG_ERR,
338 "atm_dev_lower: UNITDATA: tok=%p, state=%d\n",
339 tok, state );
340 KB_FREEALL((KBuffer *)arg1);
341 break;
345 * Hand the data off to the device
347 (*cup->cu_output)(cup, cvp, (KBuffer *)arg1);
349 break;
351 case CPCS_UABORT_INV:
352 log ( LOG_ERR,
353 "atm_dev_lower: unimplemented stack cmd 0x%x, tok=%p\n",
354 cmd, tok );
355 break;
357 default:
358 log ( LOG_ERR,
359 "atm_dev_lower: unknown stack cmd 0x%x, tok=%p\n",
360 cmd, tok );
364 return;
370 * Allocate kernel memory block
372 * This function will allocate a kernel memory block of the type specified
373 * in the flags parameter. The returned address will point to a memory
374 * block of the requested size and alignment. The memory block will also
375 * be zeroed. The alloc/free functions will manage/mask both the OS-specific
376 * kernel memory management requirements and the bookkeeping required to
377 * deal with data alignment issues.
379 * This function should not be called from interrupt level.
381 * Arguments:
382 * size size of memory block to allocate
383 * align data alignment requirement
384 * flags allocation flags (ATM_DEV_*)
386 * Returns:
387 * uaddr pointer to aligned memory block
388 * NULL unable to allocate memory
391 void *
392 atm_dev_alloc(u_int size, u_int align, u_int flags)
394 Mem_blk *mbp;
395 Mem_ent *mep;
396 u_int kalign, ksize;
397 int i;
399 crit_enter();
402 * Find a free Mem_ent
404 mep = NULL;
405 for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
406 for (i = 0; i < MEM_NMEMENT; i++) {
407 if (mbp->mb_mement[i].me_uaddr == NULL) {
408 mep = &mbp->mb_mement[i];
409 break;
415 * If there are no free Mem_ent's, then allocate a new Mem_blk
416 * and link it into the chain
418 if (mep == NULL) {
419 mbp = KM_ALLOC(sizeof(Mem_blk), M_DEVBUF,
420 M_INTWAIT | M_NULLOK);
421 if (mbp == NULL) {
422 log(LOG_ERR, "atm_dev_alloc: Mem_blk failure\n");
423 crit_exit();
424 return (NULL);
426 KM_ZERO(mbp, sizeof(Mem_blk));
428 mbp->mb_next = atm_mem_head;
429 atm_mem_head = mbp;
430 mep = mbp->mb_mement;
434 * Now we need to get the kernel's allocation alignment minimum
436 * This is obviously very OS-specific stuff
438 kalign = MINALLOCSIZE;
441 * Figure out how much memory we must allocate to satify the
442 * user's size and alignment needs
444 if (align <= kalign)
445 ksize = size;
446 else
447 ksize = size + align - kalign;
450 * Finally, go get the memory
452 if (flags & ATM_DEV_NONCACHE) {
453 mep->me_kaddr = KM_ALLOC(ksize, M_DEVBUF, M_INTWAIT | M_NULLOK);
454 } else {
455 mep->me_kaddr = KM_ALLOC(ksize, M_DEVBUF, M_INTWAIT | M_NULLOK);
458 if (mep->me_kaddr == NULL) {
459 log(LOG_ERR, "atm_dev_alloc: %skernel memory unavailable\n",
460 (flags & ATM_DEV_NONCACHE) ? "non-cacheable " : "");
461 crit_exit();
462 return (NULL);
466 * Calculate correct alignment address to pass back to user
468 mep->me_uaddr = (void *) roundup((u_int)mep->me_kaddr, align);
469 mep->me_ksize = ksize;
470 mep->me_flags = flags;
473 * Clear memory for user
475 KM_ZERO(mep->me_uaddr, size);
477 ATM_DEBUG4("atm_dev_alloc: size=%d, align=%d, flags=%d, uaddr=%p\n",
478 size, align, flags, mep->me_uaddr);
480 crit_exit();
482 return (mep->me_uaddr);
487 * Free kernel memory block
489 * This function will free a kernel memory block previously allocated by
490 * the atm_dev_alloc function.
492 * This function should not be called from interrupt level.
494 * Arguments:
495 * uaddr pointer to allocated aligned memory block
497 * Returns:
498 * none
501 void
502 atm_dev_free(volatile void *uaddr)
504 Mem_blk *mbp;
505 Mem_ent *mep;
506 int i;
508 ATM_DEBUG1("atm_dev_free: uaddr=%p\n", uaddr);
510 crit_enter();
513 * Protect ourselves...
515 if (uaddr == NULL)
516 panic("atm_dev_free: trying to free null address");
519 * Find our associated entry
521 mep = NULL;
522 for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
523 for (i = 0; i < MEM_NMEMENT; i++) {
524 if (mbp->mb_mement[i].me_uaddr == uaddr) {
525 mep = &mbp->mb_mement[i];
526 break;
532 * If we didn't find our entry, then unceremoniously let the caller
533 * know they screwed up (it certainly couldn't be a bug here...)
535 if (mep == NULL)
536 panic("atm_dev_free: trying to free unknown address");
539 * Give the memory space back to the kernel
541 if (mep->me_flags & ATM_DEV_NONCACHE) {
542 KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF);
543 } else {
544 KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF);
548 * Free our entry
550 mep->me_uaddr = NULL;
552 crit_exit();
554 return;
558 * Compress buffer chain
560 * This function will compress a supplied buffer chain into a minimum number
561 * of kernel buffers. Typically, this function will be used because the
562 * number of buffers in an output buffer chain is too large for a device's
563 * DMA capabilities. This should only be called as a last resort, since
564 * all the data copying will surely kill any hopes of decent performance.
566 * Arguments:
567 * m pointer to source buffer chain
569 * Returns:
570 * n pointer to compressed buffer chain
573 KBuffer *
574 atm_dev_compress(KBuffer *m)
576 KBuffer *n, *n0, **np;
577 int len, space;
578 caddr_t src, dst;
580 n = n0 = NULL;
581 np = &n0;
582 dst = NULL;
583 space = 0;
586 * Copy each source buffer into compressed chain
588 while (m) {
590 if (space == 0) {
593 * Allocate another buffer for compressed chain
595 KB_ALLOCEXT(n, ATM_DEV_CMPR_LG, KB_F_NOWAIT, KB_T_DATA);
596 if (n) {
597 space = ATM_DEV_CMPR_LG;
598 } else {
599 KB_ALLOC(n, ATM_DEV_CMPR_SM, KB_F_NOWAIT,
600 KB_T_DATA);
601 if (n) {
602 space = ATM_DEV_CMPR_SM;
603 } else {
605 * Unable to get any new buffers, so
606 * just return the partially compressed
607 * chain and hope...
609 *np = m;
610 break;
614 KB_HEADSET(n, 0);
615 KB_LEN(n) = 0;
616 KB_BFRSTART(n, dst, caddr_t);
618 *np = n;
619 np = &KB_NEXT(n);
623 * Copy what we can from source buffer
625 len = MIN(space, KB_LEN(m));
626 KB_DATASTART(m, src, caddr_t);
627 KM_COPY(src, dst, len);
630 * Adjust for copied data
632 dst += len;
633 space -= len;
635 KB_HEADADJ(m, -len);
636 KB_TAILADJ(n, len);
639 * If we've exhausted our current source buffer, free it
640 * and move to the next one
642 if (KB_LEN(m) == 0) {
643 KB_FREEONE(m, m);
647 return (n0);
652 * Locate VCC entry
654 * This function will return the VCC entry for a specified interface and
655 * VPI/VCI value.
657 * Arguments:
658 * cup pointer to interface unit structure
659 * vpi VPI value
660 * vci VCI value
661 * type VCC type
663 * Returns:
664 * vcp pointer to located VCC entry matching
665 * NULL no VCC found
668 Cmn_vcc *
669 atm_dev_vcc_find(Cmn_unit *cup, u_int vpi, u_int vci, u_int type)
671 Cmn_vcc *cvp;
673 crit_enter();
675 * Go find VCC
677 * (Probably should stick in a hash table some time)
679 for (cvp = cup->cu_vcc; cvp; cvp = cvp->cv_next) {
680 struct vccb *vcp;
682 vcp = cvp->cv_connvc->cvc_vcc;
683 if ((vcp->vc_vci == vci) && (vcp->vc_vpi == vpi) &&
684 ((vcp->vc_type & type) == type))
685 break;
687 crit_exit();
688 return (cvp);
692 #ifdef notdef
694 * Module unloading notification
696 * This function must be called just prior to unloading the module from
697 * memory. All allocated memory will be freed here and anything else that
698 * needs cleaning up.
700 * Arguments:
701 * none
703 * Returns:
704 * none
707 void
708 atm_unload(void)
710 Mem_blk *mbp;
711 Mem_ent *mep;
712 int i;
714 crit_enter();
717 * Free up all of our memory management storage
719 while (mbp = atm_mem_head) {
722 * Make sure users have freed up all of their memory
724 for (i = 0; i < MEM_NMEMENT; i++) {
725 if (mbp->mb_mement[i].me_uaddr != NULL) {
726 panic("atm_unload: unfreed memory");
730 atm_mem_head = mbp->mb_next;
733 * Hand this block back to the kernel
735 KM_FREE((caddr_t) mbp, sizeof(Mem_blk), M_DEVBUF);
738 crit_exit();
740 return;
742 #endif /* notdef */
746 * Print a PDU
748 * Arguments:
749 * cup pointer to device unit
750 * cvp pointer to VCC control block
751 * m pointer to pdu buffer chain
752 * msg pointer to message string
754 * Returns:
755 * none
758 void
759 atm_dev_pdu_print(Cmn_unit *cup, Cmn_vcc *cvp, KBuffer *m, char *msg)
761 char buf[128];
763 ksnprintf(buf, sizeof(buf), "%s vcc=(%d,%d)", msg,
764 cvp->cv_connvc->cvc_vcc->vc_vpi,
765 cvp->cv_connvc->cvc_vcc->vc_vci);
767 atm_pdu_print(m, buf);