Introduce cratom(), remove crcopy().
[dragonfly.git] / sys / netproto / atm / atm_device.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
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.
12  *
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.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
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.2 2003/06/17 04:28:48 dillon Exp $
28  */
29
30 /*
31  * Core ATM Services
32  * -----------------
33  *
34  * ATM device support functions
35  *
36  */
37
38 #include <netatm/kern_include.h>
39
40 /*
41  * Private structures for managing allocated kernel memory resources
42  *
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.
46  */
47 #define MEM_NMEMENT     10              /* How many Mem_ent's in a Mem_blk */
48
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) */
54 };
55 typedef struct mem_ent  Mem_ent;
56
57 /*
58  * Memory entry flags
59  */
60 #define MEF_NONCACHE    1               /* Memory is noncacheable */
61
62
63 struct mem_blk {
64         struct mem_blk  *mb_next;       /* Next block in chain */
65         Mem_ent         mb_mement[MEM_NMEMENT]; /* Allocated memory entries */
66 };
67 typedef struct mem_blk  Mem_blk;
68
69 static Mem_blk          *atm_mem_head = NULL;
70
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}
76 };
77
78
79 /*
80  * ATM Device Stack Instantiation
81  *
82  * Called at splnet.
83  *
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
91  *
92  * Returns
93  *      0               instantiation successful
94  *      err             instantiation failed - reason indicated
95  *
96  */
97 int
98 atm_dev_inst(ssp, cvcp)
99         struct stack_defn       **ssp;
100         Atm_connvc              *cvcp;
101 {
102         Cmn_unit        *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
103         Cmn_vcc         *cvp;
104         int             err;
105
106         /*
107          * Check to see if device has been initialized
108          */
109         if ((cup->cu_flags & CUF_INITED) == 0)
110                 return ( EIO );
111
112         /*
113          * Validate lower SAP
114          */
115         /*
116          * Device driver is the lowest layer - no need to validate
117          */
118
119         /*
120          * Validate PVC vpi.vci
121          */
122         if (cvcp->cvc_attr.called.addr.address_format == T_ATM_PVC_ADDR) {
123                 /*
124                  * Look through existing circuits - return error if found
125                  */
126                 Atm_addr_pvc    *pp;
127
128                 pp = (Atm_addr_pvc *)cvcp->cvc_attr.called.addr.address;
129                 if (atm_dev_vcc_find(cup, ATM_PVC_GET_VPI(pp),
130                                 ATM_PVC_GET_VCI(pp), 0))
131                         return ( EADDRINUSE );
132         }
133
134         /*
135          * Validate our SAP type
136          */
137         switch ((*(ssp+1))->sd_sap) {
138         case SAP_CPCS_AAL3_4:
139         case SAP_CPCS_AAL5:
140         case SAP_ATM:
141                 break;
142         default:
143                 return (EINVAL);
144         }
145
146         /*
147          * Allocate a VCC control block
148          */
149         if ( ( cvp = (Cmn_vcc *)atm_allocate(cup->cu_vcc_pool) ) == NULL )
150                 return ( ENOMEM );
151         
152         cvp->cv_state = CVS_INST;
153         cvp->cv_toku = (*ssp)->sd_toku;
154         cvp->cv_upper = (*ssp)->sd_upper;
155         cvp->cv_connvc = cvcp;
156
157         /*
158          * Let device have a look at the connection request
159          */
160         err = (*cup->cu_instvcc)(cup, cvp);
161         if (err) {
162                 atm_free((caddr_t)cvp);
163                 return (err);
164         }
165
166         /*
167          * Looks good so far, so link in device VCC
168          */
169         LINK2TAIL ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
170
171         /*
172          * Save my token
173          */
174         (*++ssp)->sd_toku = cvp;
175
176         /*
177          * Pass instantiation down the stack
178          */
179         /*
180          * No need - we're the lowest point.
181          */
182         /* err = (*(ssp + 1))->sd_inst(ssp, cvcp); */
183
184         /*
185          * Save the lower layer's interface info
186          */
187         /*
188          * No need - we're the lowest point
189          */
190         /* cvp->cv_lower = (*++ssp)->sd_lower; */
191         /* cvp->cv_tok1 = (*ssp)->sd_toku; */
192
193         return (0);
194 }
195
196
197 /*
198  * ATM Device Stack Command Handler
199  *
200  * Arguments
201  *      cmd             stack command code
202  *      tok             session token (Cmn_vcc)
203  *      arg1            command specific argument
204  *      arg2            command specific argument
205  *
206  * Returns
207  *      none
208  *
209  */
210 /*ARGSUSED*/
211 void
212 atm_dev_lower(cmd, tok, arg1, arg2)
213         int     cmd;
214         void    *tok;
215         int     arg1;
216         int     arg2;
217 {
218         Cmn_vcc         *cvp = (Cmn_vcc *)tok;
219         Atm_connvc      *cvcp = cvp->cv_connvc;
220         Cmn_unit        *cup = (Cmn_unit *)cvcp->cvc_attr.nif->nif_pif;
221         struct vccb     *vcp;
222         u_int           state;
223         int             s;
224
225         switch ( cmd ) {
226
227         case CPCS_INIT:
228                 /*
229                  * Sanity check
230                  */
231                 if ( cvp->cv_state != CVS_INST ) {
232                         log ( LOG_ERR,
233                                 "atm_dev_lower: INIT: tok=%p, state=%d\n",
234                                 tok, cvp->cv_state );
235                         break;
236                 }
237
238                 vcp = cvp->cv_connvc->cvc_vcc;
239
240                 /*
241                  * Validate SVC vpi.vci
242                  */
243                 if ( vcp->vc_type & VCC_SVC ) {
244
245                         if (atm_dev_vcc_find(cup, vcp->vc_vpi, vcp->vc_vci,
246                                         vcp->vc_type & (VCC_IN | VCC_OUT))
247                                                 != cvp){
248                                 log ( LOG_ERR,
249                                   "atm_dev_lower: dup SVC (%d,%d) tok=%p\n",
250                                         vcp->vc_vpi, vcp->vc_vci, tok );
251                                 atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
252                                 break;
253                         }
254                 }
255
256                 /*
257                  * Tell the device to open the VCC
258                  */
259                 cvp->cv_state = CVS_INITED;
260                 s = splimp();
261                 if ((*cup->cu_openvcc)(cup, cvp)) {
262                         atm_cm_abort(cvp->cv_connvc, &atm_dev_cause);
263                         (void) splx(s);
264                         break;
265                 }
266                 (void) splx(s);
267                 break;
268
269         case CPCS_TERM: {
270                 KBuffer         *m, *prev, *next;
271                 int             *ip;
272
273                 s = splimp();
274
275                 /*
276                  * Disconnect the VCC - ignore return code
277                  */
278                 if ((cvp->cv_state == CVS_INITED) || 
279                     (cvp->cv_state == CVS_ACTIVE)) {
280                         (void) (*cup->cu_closevcc)(cup, cvp);
281                 }
282                 cvp->cv_state = CVS_TERM;
283
284                 /*
285                  * Remove from interface list
286                  */
287                 UNLINK ( cvp, Cmn_vcc, cup->cu_vcc, cv_next );
288
289                 /*
290                  * Free any buffers from this VCC on the ATM interrupt queue
291                  */
292                 prev = NULL;
293                 for (m = atm_intrq.ifq_head; m; m = next) {
294                         next = KB_QNEXT(m);
295
296                         /*
297                          * See if this entry is for the terminating VCC
298                          */
299                         KB_DATASTART(m, ip, int *);
300                         ip++;
301                         if (*ip == (int)cvp) {
302                                 /*
303                                  * Yep, so dequeue the entry
304                                  */
305                                 if (prev == NULL)
306                                         atm_intrq.ifq_head = next;
307                                 else
308                                         KB_QNEXT(prev) = next;
309
310                                 if (next == NULL)
311                                         atm_intrq.ifq_tail = prev;
312
313                                 atm_intrq.ifq_len--;
314
315                                 /*
316                                  * Free the unwanted buffers
317                                  */
318                                 KB_FREEALL(m);
319                         } else {
320                                 prev = m;
321                         }
322                 }
323                 (void) splx(s);
324
325                 /*
326                  * Free VCC resources
327                  */
328                 (void) atm_free((caddr_t)cvp);
329                 break;
330                 }
331
332         case CPCS_UNITDATA_INV:
333
334                 /*
335                  * Sanity check
336                  *
337                  * Use temp state variable since we dont want to lock out
338                  * interrupts, but initial VC activation interrupt may
339                  * happen here, changing state somewhere in the middle.
340                  */
341                 state = cvp->cv_state;
342                 if ((state != CVS_ACTIVE) && 
343                     (state != CVS_INITED)) {
344                         log ( LOG_ERR,
345                             "atm_dev_lower: UNITDATA: tok=%p, state=%d\n",
346                                 tok, state );
347                         KB_FREEALL((KBuffer *)arg1);
348                         break;
349                 }
350
351                 /*
352                  * Hand the data off to the device
353                  */
354                 (*cup->cu_output)(cup, cvp, (KBuffer *)arg1);
355
356                 break;
357
358         case CPCS_UABORT_INV:
359                 log ( LOG_ERR,
360                     "atm_dev_lower: unimplemented stack cmd 0x%x, tok=%p\n",
361                         cmd, tok );
362                 break;
363
364         default:
365                 log ( LOG_ERR,
366                         "atm_dev_lower: unknown stack cmd 0x%x, tok=%p\n",
367                         cmd, tok );
368
369         }
370
371         return;
372 }
373
374
375
376 /*
377  * Allocate kernel memory block
378  * 
379  * This function will allocate a kernel memory block of the type specified
380  * in the flags parameter.  The returned address will point to a memory
381  * block of the requested size and alignment.  The memory block will also 
382  * be zeroed.  The alloc/free functions will manage/mask both the OS-specific 
383  * kernel memory management requirements and the bookkeeping required to
384  * deal with data alignment issues. 
385  *
386  * This function should not be called from interrupt level.
387  *
388  * Arguments:
389  *      size    size of memory block to allocate
390  *      align   data alignment requirement 
391  *      flags   allocation flags (ATM_DEV_*)
392  *
393  * Returns:
394  *      uaddr   pointer to aligned memory block
395  *      NULL    unable to allocate memory
396  *
397  */
398 void *         
399 atm_dev_alloc(size, align, flags)
400         u_int           size;
401         u_int           align;
402         u_int           flags;
403 {
404         Mem_blk         *mbp;
405         Mem_ent         *mep;
406         u_int           kalign, ksize;
407         int             s, i;
408
409         s = splimp();
410
411         /*
412          * Find a free Mem_ent
413          */
414         mep = NULL;
415         for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
416                 for (i = 0; i < MEM_NMEMENT; i++) {
417                         if (mbp->mb_mement[i].me_uaddr == NULL) {
418                                 mep = &mbp->mb_mement[i];
419                                 break;
420                         }
421                 }
422         }
423
424         /*
425          * If there are no free Mem_ent's, then allocate a new Mem_blk
426          * and link it into the chain
427          */
428         if (mep == NULL) {
429                 mbp = (Mem_blk *) KM_ALLOC(sizeof(Mem_blk), M_DEVBUF, M_NOWAIT);
430                 if (mbp == NULL) {
431                         log(LOG_ERR, "atm_dev_alloc: Mem_blk failure\n");
432                         (void) splx(s);
433                         return (NULL);
434                 }
435                 KM_ZERO(mbp, sizeof(Mem_blk));
436
437                 mbp->mb_next = atm_mem_head;
438                 atm_mem_head = mbp;
439                 mep = mbp->mb_mement;
440         }
441
442         /*
443          * Now we need to get the kernel's allocation alignment minimum
444          *
445          * This is obviously very OS-specific stuff
446          */
447 #ifdef sun
448         if (flags & ATM_DEV_NONCACHE) {
449                 /* Byte-aligned */
450                 kalign = sizeof(long);
451         } else {
452                 /* Doubleword-aligned */
453                 kalign = sizeof(double);
454         }
455 #elif (defined(BSD) && (BSD >= 199103))
456         kalign = MINALLOCSIZE;
457 #else
458         #error Unsupported/unconfigured OS
459 #endif
460
461         /*
462          * Figure out how much memory we must allocate to satify the
463          * user's size and alignment needs
464          */
465         if (align <= kalign)
466                 ksize = size;
467         else
468                 ksize = size + align - kalign;
469
470         /*
471          * Finally, go get the memory
472          */
473         if (flags & ATM_DEV_NONCACHE) {
474 #ifdef sun
475                 mep->me_kaddr = IOPBALLOC(ksize);
476 #elif defined(__i386__)
477                 mep->me_kaddr = KM_ALLOC(ksize, M_DEVBUF, M_NOWAIT);
478 #else
479                 #error Unsupported/unconfigured OS
480 #endif
481         } else {
482                 mep->me_kaddr = KM_ALLOC(ksize, M_DEVBUF, M_NOWAIT);
483         }
484
485         if (mep->me_kaddr == NULL) {
486                 log(LOG_ERR, "atm_dev_alloc: %skernel memory unavailable\n",
487                         (flags & ATM_DEV_NONCACHE) ? "non-cacheable " : "");
488                 (void) splx(s);
489                 return (NULL);
490         }
491
492         /*
493          * Calculate correct alignment address to pass back to user
494          */
495         mep->me_uaddr = (void *) roundup((u_int)mep->me_kaddr, align);
496         mep->me_ksize = ksize;
497         mep->me_flags = flags;
498
499         /*
500          * Clear memory for user
501          */
502         KM_ZERO(mep->me_uaddr, size);
503
504         ATM_DEBUG4("atm_dev_alloc: size=%d, align=%d, flags=%d, uaddr=%p\n", 
505                 size, align, flags, mep->me_uaddr);
506
507         (void) splx(s);
508
509         return (mep->me_uaddr);
510 }
511
512
513 /*
514  * Free kernel memory block
515  * 
516  * This function will free a kernel memory block previously allocated by
517  * the atm_dev_alloc function.  
518  *
519  * This function should not be called from interrupt level.
520  *
521  * Arguments:
522  *      uaddr   pointer to allocated aligned memory block
523  *
524  * Returns:
525  *      none
526  *
527  */
528 void
529 atm_dev_free(uaddr)
530         void            *uaddr;
531 {
532         Mem_blk         *mbp;
533         Mem_ent         *mep;
534         int             s, i;
535
536         ATM_DEBUG1("atm_dev_free: uaddr=%p\n", uaddr);
537
538         s = splimp();
539
540         /*
541          * Protect ourselves...
542          */
543         if (uaddr == NULL)
544                 panic("atm_dev_free: trying to free null address");
545
546         /*
547          * Find our associated entry
548          */
549         mep = NULL;
550         for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
551                 for (i = 0; i < MEM_NMEMENT; i++) {
552                         if (mbp->mb_mement[i].me_uaddr == uaddr) {
553                                 mep = &mbp->mb_mement[i];
554                                 break;
555                         }
556                 }
557         }
558
559         /*
560          * If we didn't find our entry, then unceremoniously let the caller
561          * know they screwed up (it certainly couldn't be a bug here...)
562          */
563         if (mep == NULL)
564                 panic("atm_dev_free: trying to free unknown address");
565         
566         /*
567          * Give the memory space back to the kernel
568          */
569         if (mep->me_flags & ATM_DEV_NONCACHE) {
570 #ifdef sun
571                 IOPBFREE(mep->me_kaddr, mep->me_ksize);
572 #elif defined(__i386__)
573                 KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF);
574 #else
575                 #error Unsupported/unconfigured OS
576 #endif
577         } else {
578                 KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF);
579         }
580
581         /*
582          * Free our entry
583          */
584         mep->me_uaddr = NULL;
585
586         (void) splx(s);
587
588         return;
589 }
590
591
592 #ifdef  sun4m
593
594 typedef int (*func_t)();
595
596 /*
597  * Map an address into DVMA space
598  * 
599  * This function will take a kernel virtual address and map it to
600  * a DMA virtual address which can be used during SBus DMA cycles.
601  *
602  * Arguments:
603  *      addr    kernel virtual address
604  *      len     length of DVMA space requested
605  *      flags   allocation flags (ATM_DEV_*)
606  *
607  * Returns:
608  *      a       DVMA address
609  *      NULL    unable to map into DMA space
610  *
611  */
612 void *
613 atm_dma_map(addr, len, flags)
614         caddr_t addr;
615         int     len;
616         int     flags;
617 {
618         if (flags & ATM_DEV_NONCACHE)
619                 /*
620                  * Non-cacheable memory is already DMA'able
621                  */
622                 return ((void *)addr);
623         else
624                 return ((void *)mb_nbmapalloc(bigsbusmap, addr, len,
625                         MDR_BIGSBUS|MB_CANTWAIT, (func_t)NULL, (caddr_t)NULL));
626 }
627
628
629 /*
630  * Free a DVMA map address
631  * 
632  * This function will free DVMA map resources (addresses) previously
633  * allocated with atm_dma_map().
634  *
635  * Arguments:
636  *      addr    DMA virtual address
637  *      flags   allocation flags (ATM_DEV_*)
638  *
639  * Returns:
640  *      none
641  *
642  */
643 void
644 atm_dma_free(addr, flags)
645         caddr_t addr;
646         int     flags;
647 {
648         if ((flags & ATM_DEV_NONCACHE) == 0)
649                 mb_mapfree(bigsbusmap, (int)&addr);
650
651         return;
652 }
653 #endif  /* sun4m */
654
655
656 /*
657  * Compress buffer chain
658  * 
659  * This function will compress a supplied buffer chain into a minimum number
660  * of kernel buffers.  Typically, this function will be used because the
661  * number of buffers in an output buffer chain is too large for a device's
662  * DMA capabilities.  This should only be called as a last resort, since
663  * all the data copying will surely kill any hopes of decent performance.
664  *
665  * Arguments:
666  *      m       pointer to source buffer chain
667  *
668  * Returns:
669  *      n       pointer to compressed buffer chain
670  *
671  */
672 KBuffer *         
673 atm_dev_compress(m)
674         KBuffer         *m;
675 {
676         KBuffer         *n, *n0, **np;
677         int             len, space;
678         caddr_t         src, dst;
679
680         n = n0 = NULL;
681         np = &n0;
682         dst = NULL;
683         space = 0;
684
685         /*
686          * Copy each source buffer into compressed chain
687          */
688         while (m) {
689
690                 if (space == 0) {
691
692                         /*
693                          * Allocate another buffer for compressed chain
694                          */
695                         KB_ALLOCEXT(n, ATM_DEV_CMPR_LG, KB_F_NOWAIT, KB_T_DATA);
696                         if (n) {
697                                 space = ATM_DEV_CMPR_LG;
698                         } else {
699                                 KB_ALLOC(n, ATM_DEV_CMPR_SM, KB_F_NOWAIT, 
700                                         KB_T_DATA);
701                                 if (n) {
702                                         space = ATM_DEV_CMPR_SM;
703                                 } else {
704                                         /*
705                                          * Unable to get any new buffers, so
706                                          * just return the partially compressed
707                                          * chain and hope...
708                                          */
709                                         *np = m;
710                                         break;
711                                 }
712                         }
713
714                         KB_HEADSET(n, 0);
715                         KB_LEN(n) = 0;
716                         KB_BFRSTART(n, dst, caddr_t);
717
718                         *np = n;
719                         np = &KB_NEXT(n);
720                 }
721
722                 /*
723                  * Copy what we can from source buffer
724                  */
725                 len = MIN(space, KB_LEN(m));
726                 KB_DATASTART(m, src, caddr_t);
727                 KM_COPY(src, dst, len);
728
729                 /*
730                  * Adjust for copied data
731                  */
732                 dst += len;
733                 space -= len;
734
735                 KB_HEADADJ(m, -len);
736                 KB_TAILADJ(n, len);
737
738                 /*
739                  * If we've exhausted our current source buffer, free it
740                  * and move to the next one
741                  */
742                 if (KB_LEN(m) == 0) {
743                         KB_FREEONE(m, m);
744                 }
745         }
746
747         return (n0);
748 }
749
750
751 /*
752  * Locate VCC entry
753  * 
754  * This function will return the VCC entry for a specified interface and
755  * VPI/VCI value.
756  *
757  * Arguments:
758  *      cup     pointer to interface unit structure
759  *      vpi     VPI value
760  *      vci     VCI value
761  *      type    VCC type
762  *
763  * Returns:
764  *      vcp     pointer to located VCC entry matching
765  *      NULL    no VCC found
766  *
767  */
768 Cmn_vcc *
769 atm_dev_vcc_find(cup, vpi, vci, type)
770         Cmn_unit        *cup;
771         u_int           vpi;
772         u_int           vci;
773         u_int           type;
774 {
775         Cmn_vcc         *cvp;
776         int             s = splnet();
777
778         /*
779          * Go find VCC
780          *
781          * (Probably should stick in a hash table some time)
782          */
783         for (cvp = cup->cu_vcc; cvp; cvp = cvp->cv_next) {
784                 struct vccb     *vcp;
785
786                 vcp = cvp->cv_connvc->cvc_vcc;
787                 if ((vcp->vc_vci == vci) && (vcp->vc_vpi == vpi) && 
788                     ((vcp->vc_type & type) == type))
789                         break;
790         }
791
792         (void) splx(s);
793         return (cvp);
794 }
795
796
797 #ifdef notdef
798 /*
799  * Module unloading notification
800  * 
801  * This function must be called just prior to unloading the module from 
802  * memory.  All allocated memory will be freed here and anything else that
803  * needs cleaning up.
804  *
805  * Arguments:
806  *      none
807  *
808  * Returns:
809  *      none
810  *
811  */
812 void
813 atm_unload()
814 {
815         Mem_blk         *mbp;
816         Mem_ent         *mep;
817         int             s, i;
818
819         s = splimp();
820
821         /*
822          * Free up all of our memory management storage
823          */
824         while (mbp = atm_mem_head) {
825
826                 /*
827                  * Make sure users have freed up all of their memory
828                  */
829                 for (i = 0; i < MEM_NMEMENT; i++) {
830                         if (mbp->mb_mement[i].me_uaddr != NULL) {
831                                 panic("atm_unload: unfreed memory");
832                         }
833                 }
834
835                 atm_mem_head = mbp->mb_next;
836
837                 /*
838                  * Hand this block back to the kernel
839                  */
840                 KM_FREE((caddr_t) mbp, sizeof(Mem_blk), M_DEVBUF);
841         }
842
843         (void) splx(s);
844
845         return;
846 }
847 #endif  /* notdef */
848
849
850 /*
851  * Print a PDU
852  * 
853  * Arguments:
854  *      cup     pointer to device unit
855  *      cvp     pointer to VCC control block
856  *      m       pointer to pdu buffer chain
857  *      msg     pointer to message string
858  *
859  * Returns:
860  *      none
861  *
862  */
863 void
864 atm_dev_pdu_print(cup, cvp, m, msg)
865         Cmn_unit        *cup;
866         Cmn_vcc         *cvp;
867         KBuffer         *m;
868         char            *msg;
869 {
870         char            buf[128];
871
872         snprintf(buf, sizeof(buf), "%s vcc=(%d,%d)", msg, 
873                 cvp->cv_connvc->cvc_vcc->vc_vpi, 
874                 cvp->cv_connvc->cvc_vcc->vc_vci);
875
876         atm_pdu_print(m, buf);
877 }
878