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