Push the lwkt_replymsg() up one level from netisr_service_loop() to
[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.4 2003/08/07 21:17:34 dillon Exp $
28  */
29
30 /*
31  * Core ATM Services
32  * -----------------
33  *
34  * ATM device support functions
35  *
36  */
37
38 #include "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(volatile void *uaddr)
530 {
531         Mem_blk         *mbp;
532         Mem_ent         *mep;
533         int             s, i;
534
535         ATM_DEBUG1("atm_dev_free: uaddr=%p\n", uaddr);
536
537         s = splimp();
538
539         /*
540          * Protect ourselves...
541          */
542         if (uaddr == NULL)
543                 panic("atm_dev_free: trying to free null address");
544
545         /*
546          * Find our associated entry
547          */
548         mep = NULL;
549         for (mbp = atm_mem_head; mbp && mep == NULL; mbp = mbp->mb_next) {
550                 for (i = 0; i < MEM_NMEMENT; i++) {
551                         if (mbp->mb_mement[i].me_uaddr == uaddr) {
552                                 mep = &mbp->mb_mement[i];
553                                 break;
554                         }
555                 }
556         }
557
558         /*
559          * If we didn't find our entry, then unceremoniously let the caller
560          * know they screwed up (it certainly couldn't be a bug here...)
561          */
562         if (mep == NULL)
563                 panic("atm_dev_free: trying to free unknown address");
564         
565         /*
566          * Give the memory space back to the kernel
567          */
568         if (mep->me_flags & ATM_DEV_NONCACHE) {
569 #ifdef sun
570                 IOPBFREE(mep->me_kaddr, mep->me_ksize);
571 #elif defined(__i386__)
572                 KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF);
573 #else
574                 #error Unsupported/unconfigured OS
575 #endif
576         } else {
577                 KM_FREE(mep->me_kaddr, mep->me_ksize, M_DEVBUF);
578         }
579
580         /*
581          * Free our entry
582          */
583         mep->me_uaddr = NULL;
584
585         (void) splx(s);
586
587         return;
588 }
589
590
591 #ifdef  sun4m
592
593 typedef int (*func_t)();
594
595 /*
596  * Map an address into DVMA space
597  * 
598  * This function will take a kernel virtual address and map it to
599  * a DMA virtual address which can be used during SBus DMA cycles.
600  *
601  * Arguments:
602  *      addr    kernel virtual address
603  *      len     length of DVMA space requested
604  *      flags   allocation flags (ATM_DEV_*)
605  *
606  * Returns:
607  *      a       DVMA address
608  *      NULL    unable to map into DMA space
609  *
610  */
611 void *
612 atm_dma_map(addr, len, flags)
613         caddr_t addr;
614         int     len;
615         int     flags;
616 {
617         if (flags & ATM_DEV_NONCACHE)
618                 /*
619                  * Non-cacheable memory is already DMA'able
620                  */
621                 return ((void *)addr);
622         else
623                 return ((void *)mb_nbmapalloc(bigsbusmap, addr, len,
624                         MDR_BIGSBUS|MB_CANTWAIT, (func_t)NULL, (caddr_t)NULL));
625 }
626
627
628 /*
629  * Free a DVMA map address
630  * 
631  * This function will free DVMA map resources (addresses) previously
632  * allocated with atm_dma_map().
633  *
634  * Arguments:
635  *      addr    DMA virtual address
636  *      flags   allocation flags (ATM_DEV_*)
637  *
638  * Returns:
639  *      none
640  *
641  */
642 void
643 atm_dma_free(addr, flags)
644         caddr_t addr;
645         int     flags;
646 {
647         if ((flags & ATM_DEV_NONCACHE) == 0)
648                 mb_mapfree(bigsbusmap, (int)&addr);
649
650         return;
651 }
652 #endif  /* sun4m */
653
654
655 /*
656  * Compress buffer chain
657  * 
658  * This function will compress a supplied buffer chain into a minimum number
659  * of kernel buffers.  Typically, this function will be used because the
660  * number of buffers in an output buffer chain is too large for a device's
661  * DMA capabilities.  This should only be called as a last resort, since
662  * all the data copying will surely kill any hopes of decent performance.
663  *
664  * Arguments:
665  *      m       pointer to source buffer chain
666  *
667  * Returns:
668  *      n       pointer to compressed buffer chain
669  *
670  */
671 KBuffer *         
672 atm_dev_compress(m)
673         KBuffer         *m;
674 {
675         KBuffer         *n, *n0, **np;
676         int             len, space;
677         caddr_t         src, dst;
678
679         n = n0 = NULL;
680         np = &n0;
681         dst = NULL;
682         space = 0;
683
684         /*
685          * Copy each source buffer into compressed chain
686          */
687         while (m) {
688
689                 if (space == 0) {
690
691                         /*
692                          * Allocate another buffer for compressed chain
693                          */
694                         KB_ALLOCEXT(n, ATM_DEV_CMPR_LG, KB_F_NOWAIT, KB_T_DATA);
695                         if (n) {
696                                 space = ATM_DEV_CMPR_LG;
697                         } else {
698                                 KB_ALLOC(n, ATM_DEV_CMPR_SM, KB_F_NOWAIT, 
699                                         KB_T_DATA);
700                                 if (n) {
701                                         space = ATM_DEV_CMPR_SM;
702                                 } else {
703                                         /*
704                                          * Unable to get any new buffers, so
705                                          * just return the partially compressed
706                                          * chain and hope...
707                                          */
708                                         *np = m;
709                                         break;
710                                 }
711                         }
712
713                         KB_HEADSET(n, 0);
714                         KB_LEN(n) = 0;
715                         KB_BFRSTART(n, dst, caddr_t);
716
717                         *np = n;
718                         np = &KB_NEXT(n);
719                 }
720
721                 /*
722                  * Copy what we can from source buffer
723                  */
724                 len = MIN(space, KB_LEN(m));
725                 KB_DATASTART(m, src, caddr_t);
726                 KM_COPY(src, dst, len);
727
728                 /*
729                  * Adjust for copied data
730                  */
731                 dst += len;
732                 space -= len;
733
734                 KB_HEADADJ(m, -len);
735                 KB_TAILADJ(n, len);
736
737                 /*
738                  * If we've exhausted our current source buffer, free it
739                  * and move to the next one
740                  */
741                 if (KB_LEN(m) == 0) {
742                         KB_FREEONE(m, m);
743                 }
744         }
745
746         return (n0);
747 }
748
749
750 /*
751  * Locate VCC entry
752  * 
753  * This function will return the VCC entry for a specified interface and
754  * VPI/VCI value.
755  *
756  * Arguments:
757  *      cup     pointer to interface unit structure
758  *      vpi     VPI value
759  *      vci     VCI value
760  *      type    VCC type
761  *
762  * Returns:
763  *      vcp     pointer to located VCC entry matching
764  *      NULL    no VCC found
765  *
766  */
767 Cmn_vcc *
768 atm_dev_vcc_find(cup, vpi, vci, type)
769         Cmn_unit        *cup;
770         u_int           vpi;
771         u_int           vci;
772         u_int           type;
773 {
774         Cmn_vcc         *cvp;
775         int             s = splnet();
776
777         /*
778          * Go find VCC
779          *
780          * (Probably should stick in a hash table some time)
781          */
782         for (cvp = cup->cu_vcc; cvp; cvp = cvp->cv_next) {
783                 struct vccb     *vcp;
784
785                 vcp = cvp->cv_connvc->cvc_vcc;
786                 if ((vcp->vc_vci == vci) && (vcp->vc_vpi == vpi) && 
787                     ((vcp->vc_type & type) == type))
788                         break;
789         }
790
791         (void) splx(s);
792         return (cvp);
793 }
794
795
796 #ifdef notdef
797 /*
798  * Module unloading notification
799  * 
800  * This function must be called just prior to unloading the module from 
801  * memory.  All allocated memory will be freed here and anything else that
802  * needs cleaning up.
803  *
804  * Arguments:
805  *      none
806  *
807  * Returns:
808  *      none
809  *
810  */
811 void
812 atm_unload()
813 {
814         Mem_blk         *mbp;
815         Mem_ent         *mep;
816         int             s, i;
817
818         s = splimp();
819
820         /*
821          * Free up all of our memory management storage
822          */
823         while (mbp = atm_mem_head) {
824
825                 /*
826                  * Make sure users have freed up all of their memory
827                  */
828                 for (i = 0; i < MEM_NMEMENT; i++) {
829                         if (mbp->mb_mement[i].me_uaddr != NULL) {
830                                 panic("atm_unload: unfreed memory");
831                         }
832                 }
833
834                 atm_mem_head = mbp->mb_next;
835
836                 /*
837                  * Hand this block back to the kernel
838                  */
839                 KM_FREE((caddr_t) mbp, sizeof(Mem_blk), M_DEVBUF);
840         }
841
842         (void) splx(s);
843
844         return;
845 }
846 #endif  /* notdef */
847
848
849 /*
850  * Print a PDU
851  * 
852  * Arguments:
853  *      cup     pointer to device unit
854  *      cvp     pointer to VCC control block
855  *      m       pointer to pdu buffer chain
856  *      msg     pointer to message string
857  *
858  * Returns:
859  *      none
860  *
861  */
862 void
863 atm_dev_pdu_print(cup, cvp, m, msg)
864         Cmn_unit        *cup;
865         Cmn_vcc         *cvp;
866         KBuffer         *m;
867         char            *msg;
868 {
869         char            buf[128];
870
871         snprintf(buf, sizeof(buf), "%s vcc=(%d,%d)", msg, 
872                 cvp->cv_connvc->cvc_vcc->vc_vpi, 
873                 cvp->cv_connvc->cvc_vcc->vc_vci);
874
875         atm_pdu_print(m, buf);
876 }
877