kernel: Sync ACPICA with Intel's version 20140424.
[dragonfly.git] / sys / netproto / atm / atm_subr.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_subr.c,v 1.7 2000/02/13 03:31:59 peter Exp $
27  *      @(#) $DragonFly: src/sys/netproto/atm/atm_subr.c,v 1.23 2008/09/24 14:26:39 sephe Exp $
28  */
29
30 /*
31  * Core ATM Services
32  * -----------------
33  *
34  * Miscellaneous ATM subroutines
35  *
36  */
37
38 #include "kern_include.h"
39
40 /*
41  * Global variables
42  */
43 struct atm_pif          *atm_interface_head = NULL;
44 struct atm_ncm          *atm_netconv_head = NULL;
45 Atm_endpoint            *atm_endpoints[ENDPT_MAX+1] = {NULL};
46 struct sp_info          *atm_pool_head = NULL;
47 struct stackq_entry     *atm_stackq_head = NULL, *atm_stackq_tail;
48 struct atm_sock_stat    atm_sock_stat = { { 0 } };
49 int                     atm_init = 0;
50 int                     atm_debug = 0;
51 int                     atm_dev_print = 0;
52 int                     atm_print_data = 0;
53 int                     atm_version = ATM_VERSION;
54 struct timeval          atm_debugtime = {0, 0};
55 struct ifqueue          atm_intrq;
56
57 struct sp_info  atm_attributes_pool = {
58         "atm attributes pool",          /* si_name */
59         sizeof(Atm_attributes),         /* si_blksiz */
60         10,                             /* si_blkcnt */
61         100                             /* si_maxallow */
62 };
63
64 static struct callout atm_timexp_ch;
65
66 /*
67  * Local functions
68  */
69 static void     atm_compact (struct atm_time *);
70 static KTimeout_ret     atm_timexp (void *);
71 static void     atm_intr(netmsg_t msg);
72
73 /*
74  * Local variables
75  */
76 static struct atm_time  *atm_timeq = NULL;
77 static struct atm_time  atm_compactimer = {0, 0};
78
79 static struct sp_info   atm_stackq_pool = {
80         "Service stack queue pool",     /* si_name */
81         sizeof(struct stackq_entry),    /* si_blksiz */
82         10,                             /* si_blkcnt */
83         10                              /* si_maxallow */
84 };
85
86
87 /*
88  * Initialize ATM kernel
89  * 
90  * Performs any initialization required before things really get underway.
91  * Called from ATM domain initialization or from first registration function 
92  * which gets called.
93  *
94  * Arguments:
95  *      none
96  *
97  * Returns:
98  *      none
99  *
100  */
101 void
102 atm_initialize(void)
103 {
104         /*
105          * Never called from interrupts, so no locking needed
106          */
107         if (atm_init)
108                 return;
109         atm_init = 1;
110
111         atm_intrq.ifq_maxlen = ATM_INTRQ_MAX;
112         netisr_register(NETISR_ATM, atm_intr, NULL);
113
114         /*
115          * Initialize subsystems
116          */
117         atm_aal5_init();
118
119         /*
120          * Prime the timer
121          */
122         callout_init(&atm_timexp_ch);
123         callout_reset(&atm_timexp_ch, hz / ATM_HZ, atm_timexp, NULL);
124
125         /*
126          * Start the compaction timer
127          */
128         atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
129 }
130
131
132 /*
133  * Allocate a Control Block
134  * 
135  * Gets a new control block allocated from the specified storage pool, 
136  * acquiring memory for new pool chunks if required.  The returned control
137  * block's contents will be cleared.
138  *
139  * Arguments:
140  *      sip     pointer to sp_info for storage pool
141  *
142  * Returns:
143  *      addr    pointer to allocated control block
144  *      0       allocation failed
145  *
146  */
147 void *
148 atm_allocate(struct sp_info *sip)
149 {
150         void            *bp;
151         struct sp_chunk *scp;
152         struct sp_link  *slp;
153
154         crit_enter();
155
156         /*
157          * Count calls
158          */
159         sip->si_allocs++;
160
161         /*
162          * Are there any free in the pool?
163          */
164         if (sip->si_free) {
165
166                 /*
167                  * Find first chunk with a free block
168                  */
169                 for (scp = sip->si_poolh; scp; scp = scp->sc_next) {
170                         if (scp->sc_freeh != NULL)
171                                 break;
172                 }
173
174         } else {
175
176                 /*
177                  * No free blocks - have to allocate a new
178                  * chunk (but put a limit to this)
179                  */
180                 struct sp_link  *slp_next;
181                 int     i;
182
183                 /*
184                  * First time for this pool??
185                  */
186                 if (sip->si_chunksiz == 0) {
187                         size_t  n;
188
189                         /*
190                          * Initialize pool information
191                          */
192                         n = sizeof(struct sp_chunk) +
193                                 sip->si_blkcnt * 
194                                 (sip->si_blksiz + sizeof(struct sp_link));
195                         sip->si_chunksiz = roundup(n, SPOOL_ROUNDUP);
196
197                         /*
198                          * Place pool on kernel chain
199                          */
200                         LINK2TAIL(sip, struct sp_info, atm_pool_head, si_next);
201                 }
202
203                 if (sip->si_chunks >= sip->si_maxallow) {
204                         sip->si_fails++;
205                         crit_exit();
206                         return (NULL);
207                 }
208
209                 scp = KM_ALLOC(sip->si_chunksiz, M_DEVBUF,
210                                 M_INTWAIT | M_NULLOK);
211                 if (scp == NULL) {
212                         sip->si_fails++;
213                         crit_exit();
214                         return (NULL);
215                 }
216                 scp->sc_next = NULL;
217                 scp->sc_info = sip;
218                 scp->sc_magic = SPOOL_MAGIC;
219                 scp->sc_used = 0;
220
221                 /*
222                  * Divy up chunk into free blocks
223                  */
224                 slp = (struct sp_link *)(scp + 1);
225                 scp->sc_freeh = slp;
226
227                 for (i = sip->si_blkcnt; i > 1; i--) { 
228                         slp_next = (struct sp_link *)((caddr_t)(slp + 1) + 
229                                         sip->si_blksiz);
230                         slp->sl_u.slu_next = slp_next;
231                         slp = slp_next;
232                 }
233                 slp->sl_u.slu_next = NULL;
234                 scp->sc_freet = slp;
235
236                 /*
237                  * Add new chunk to end of pool
238                  */
239                 if (sip->si_poolh)
240                         sip->si_poolt->sc_next = scp;
241                 else
242                         sip->si_poolh = scp;
243                 sip->si_poolt = scp;
244                 
245                 sip->si_chunks++;
246                 sip->si_total += sip->si_blkcnt;
247                 sip->si_free += sip->si_blkcnt;
248                 if (sip->si_chunks > sip->si_maxused)
249                         sip->si_maxused = sip->si_chunks;
250         }
251
252         /*
253          * Allocate the first free block in chunk
254          */
255         slp = scp->sc_freeh;
256         scp->sc_freeh = slp->sl_u.slu_next;
257         scp->sc_used++;
258         sip->si_free--;
259         bp = (slp + 1);
260
261         /*
262          * Save link back to pool chunk
263          */
264         slp->sl_u.slu_chunk = scp;
265
266         /*
267          * Clear out block
268          */
269         KM_ZERO(bp, sip->si_blksiz);
270
271         crit_exit();
272         return (bp);
273 }
274
275
276 /*
277  * Free a Control Block
278  * 
279  * Returns a previously allocated control block back to the owners 
280  * storage pool.  
281  *
282  * Arguments:
283  *      bp      pointer to block to be freed
284  *
285  * Returns:
286  *      none
287  *
288  */
289 void
290 atm_free(void *bp)
291 {
292         struct sp_info  *sip;
293         struct sp_chunk *scp;
294         struct sp_link  *slp;
295
296         crit_enter();
297
298         /*
299          * Get containing chunk and pool info
300          */
301         slp = (struct sp_link *)bp;
302         slp--;
303         scp = slp->sl_u.slu_chunk;
304         if (scp->sc_magic != SPOOL_MAGIC)
305                 panic("atm_free: chunk magic missing");
306         sip = scp->sc_info;
307
308         /*
309          * Add block to free chain
310          */
311         if (scp->sc_freeh) {
312                 scp->sc_freet->sl_u.slu_next = slp;
313                 scp->sc_freet = slp;
314         } else
315                 scp->sc_freeh = scp->sc_freet = slp;
316         slp->sl_u.slu_next = NULL;
317         sip->si_free++;
318         scp->sc_used--;
319
320         crit_exit();
321         return;
322 }
323
324
325 /*
326  * Storage Pool Compaction
327  * 
328  * Called periodically in order to perform compaction of the
329  * storage pools.  Each pool will be checked to see if any chunks 
330  * can be freed, taking some care to avoid freeing too many chunks
331  * in order to avoid memory thrashing.
332  *
333  * Called from a critical section.
334  *
335  * Arguments:
336  *      tip     pointer to timer control block (atm_compactimer)
337  *
338  * Returns:
339  *      none
340  *
341  */
342 static void
343 atm_compact(struct atm_time *tip)
344 {
345         struct sp_info  *sip;
346         struct sp_chunk *scp;
347         int             i;
348         struct sp_chunk *scp_prev;
349
350         /*
351          * Check out all storage pools
352          */
353         for (sip = atm_pool_head; sip; sip = sip->si_next) {
354
355                 /*
356                  * Always keep a minimum number of chunks around
357                  */
358                 if (sip->si_chunks <= SPOOL_MIN_CHUNK)
359                         continue;
360
361                 /*
362                  * Maximum chunks to free at one time will leave
363                  * pool with at least 50% utilization, but never
364                  * go below minimum chunk count.
365                  */
366                 i = ((sip->si_free * 2) - sip->si_total) / sip->si_blkcnt;
367                 i = MIN(i, sip->si_chunks - SPOOL_MIN_CHUNK);
368
369                 /*
370                  * Look for chunks to free
371                  */
372                 scp_prev = NULL;
373                 for (scp = sip->si_poolh; scp && i > 0; ) {
374
375                         if (scp->sc_used == 0) {
376
377                                 /*
378                                  * Found a chunk to free, so do it
379                                  */
380                                 if (scp_prev) {
381                                         scp_prev->sc_next = scp->sc_next;
382                                         if (sip->si_poolt == scp)
383                                                 sip->si_poolt = scp_prev;
384                                 } else
385                                         sip->si_poolh = scp->sc_next;
386
387                                 KM_FREE((caddr_t)scp, sip->si_chunksiz,
388                                         M_DEVBUF);
389
390                                 /*
391                                  * Update pool controls
392                                  */
393                                 sip->si_chunks--;
394                                 sip->si_total -= sip->si_blkcnt;
395                                 sip->si_free -= sip->si_blkcnt;
396                                 i--;
397                                 if (scp_prev)
398                                         scp = scp_prev->sc_next;
399                                 else
400                                         scp = sip->si_poolh;
401                         } else {
402                                 scp_prev = scp;
403                                 scp = scp->sc_next;
404                         }
405                 }
406         }
407
408         /*
409          * Restart the compaction timer
410          */
411         atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
412
413         return;
414 }
415
416
417 /*
418  * Release a Storage Pool
419  * 
420  * Frees all dynamic storage acquired for a storage pool.
421  * This function is normally called just prior to a module's unloading.
422  *
423  * Arguments:
424  *      sip     pointer to sp_info for storage pool
425  *
426  * Returns:
427  *      none
428  *
429  */
430 void
431 atm_release_pool(struct sp_info *sip)
432 {
433         struct sp_chunk *scp, *scp_next;
434
435         crit_enter();
436         /*
437          * Free each chunk in pool
438          */
439         for (scp = sip->si_poolh; scp; scp = scp_next) {
440
441                 /*
442                  * Check for memory leaks
443                  */
444                 if (scp->sc_used)
445                         panic("atm_release_pool: unfreed blocks");
446
447                 scp_next = scp->sc_next;
448
449                 KM_FREE((caddr_t)scp, sip->si_chunksiz, M_DEVBUF);
450         }
451
452         /*
453          * Update pool controls
454          */
455         sip->si_poolh = NULL;
456         sip->si_chunks = 0;
457         sip->si_total = 0;
458         sip->si_free = 0;
459
460         /*
461          * Unlink pool from active chain
462          */
463         sip->si_chunksiz = 0;
464         UNLINK(sip, struct sp_info, atm_pool_head, si_next);
465         crit_exit();
466         return;
467 }
468
469
470 /*
471  * Handle timer tick expiration
472  * 
473  * Decrement tick count in first block on timer queue.  If there
474  * are blocks with expired timers, call their timeout function.
475  * This function is called ATM_HZ times per second.
476  *
477  * Arguments:
478  *      arg     argument passed on timeout() call
479  *
480  * Returns:
481  *      none
482  *
483  */
484 static KTimeout_ret
485 atm_timexp(void *arg)
486 {
487         struct atm_time *tip;
488
489         crit_enter();
490         /*
491          * Decrement tick count
492          */
493         if (((tip = atm_timeq) == NULL) || (--tip->ti_ticks > 0)) {
494                 goto restart;
495         }
496
497         /*
498          * Stack queue should have been drained
499          */
500 #ifdef DIAGNOSTIC
501         if (atm_stackq_head != NULL)
502                 panic("atm_timexp: stack queue not empty");
503 #endif
504
505         /*
506          * Dispatch expired timers
507          */
508         while (((tip = atm_timeq) != NULL) && (tip->ti_ticks == 0)) {
509                 void    (*func)(struct atm_time *);
510
511                 /*
512                  * Remove expired block from queue
513                  */
514                 atm_timeq = tip->ti_next;
515                 tip->ti_flag &= ~TIF_QUEUED;
516
517                 /*
518                  * Call timeout handler (with network interrupts locked out)
519                  */
520                 func = tip->ti_func;
521                 (*func)(tip);
522
523                 /*
524                  * Drain any deferred calls
525                  */
526                 STACK_DRAIN();
527         }
528
529 restart:
530         /*
531          * Restart the timer
532          */
533         crit_exit();
534         callout_reset(&atm_timexp_ch, hz / ATM_HZ, atm_timexp, NULL);
535 }
536
537
538 /*
539  * Schedule a control block timeout
540  * 
541  * Place the supplied timer control block on the timer queue.  The
542  * function (func) will be called in 't' timer ticks with the
543  * control block address as its only argument.  There are ATM_HZ
544  * timer ticks per second.  The ticks value stored in each block is
545  * a delta of the number of ticks from the previous block in the queue.
546  * Thus, for each tick interval, only the first block in the queue 
547  * needs to have its tick value decremented.
548  *
549  * Arguments:
550  *      tip     pointer to timer control block
551  *      t       number of timer ticks until expiration
552  *      func    pointer to function to call at expiration 
553  *
554  * Returns:
555  *      none
556  *
557  */
558 void
559 atm_timeout(struct atm_time *tip, int t, void (*func)(struct atm_time *))
560 {
561         struct atm_time *tip1, *tip2;
562
563
564         /*
565          * Check for double queueing error
566          */
567         if (tip->ti_flag & TIF_QUEUED)
568                 panic("atm_timeout: double queueing");
569
570         /*
571          * Make sure we delay at least a little bit
572          */
573         if (t <= 0)
574                 t = 1;
575
576         /*
577          * Find out where we belong on the queue
578          */
579         crit_enter();
580         for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2->ti_ticks <= t); 
581                                             tip1 = tip2, tip2 = tip1->ti_next) {
582                 t -= tip2->ti_ticks;
583         }
584
585         /*
586          * Place ourselves on queue and update timer deltas
587          */
588         if (tip1 == NULL)
589                 atm_timeq = tip;
590         else
591                 tip1->ti_next = tip;
592         tip->ti_next = tip2;
593
594         if (tip2)
595                 tip2->ti_ticks -= t;
596         
597         /*
598          * Setup timer block
599          */
600         tip->ti_flag |= TIF_QUEUED;
601         tip->ti_ticks = t;
602         tip->ti_func = func;
603
604         crit_exit();
605         return;
606 }
607
608
609 /*
610  * Cancel a timeout
611  * 
612  * Remove the supplied timer control block from the timer queue.
613  *
614  * Arguments:
615  *      tip     pointer to timer control block
616  *
617  * Returns:
618  *      0       control block successfully dequeued
619  *      1       control block not on timer queue
620  *
621  */
622 int
623 atm_untimeout(struct atm_time *tip)
624 {
625         struct atm_time *tip1, *tip2;
626
627         /*
628          * Is control block queued?
629          */
630         if ((tip->ti_flag & TIF_QUEUED) == 0)
631                 return(1);
632
633         /*
634          * Find control block on the queue
635          */
636         crit_enter();
637         for (tip1 = NULL, tip2 = atm_timeq; tip2 && (tip2 != tip); 
638                                             tip1 = tip2, tip2 = tip1->ti_next) {
639         }
640
641         if (tip2 == NULL) {
642                 crit_exit();
643                 return (1);
644         }
645
646         /*
647          * Remove block from queue and update timer deltas
648          */
649         tip2 = tip->ti_next;
650         if (tip1 == NULL)
651                 atm_timeq = tip2;
652         else
653                 tip1->ti_next = tip2;
654
655         if (tip2)
656                 tip2->ti_ticks += tip->ti_ticks;
657         
658         /*
659          * Reset timer block
660          */
661         tip->ti_flag &= ~TIF_QUEUED;
662
663         crit_exit();
664         return (0);
665 }
666
667
668 /*
669  * Queue a Stack Call 
670  * 
671  * Queues a stack call which must be deferred to the global stack queue.
672  * The call parameters are stored in entries which are allocated from the
673  * stack queue storage pool.
674  *
675  * Arguments:
676  *      cmd     stack command
677  *      func    destination function
678  *      token   destination layer's token
679  *      cvp     pointer to  connection vcc
680  *      arg1    command argument
681  *      arg2    command argument
682  *
683  * Returns:
684  *      0       call queued
685  *      errno   call not queued - reason indicated
686  *
687  */
688 int
689 atm_stack_enq(int cmd, void (*func)(int, void *, int, int), void *token,
690               Atm_connvc *cvp, int arg1, int arg2)
691 {
692         struct stackq_entry     *sqp;
693
694         crit_enter();
695
696         /*
697          * Get a new queue entry for this call
698          */
699         sqp = (struct stackq_entry *)atm_allocate(&atm_stackq_pool);
700         if (sqp == NULL) {
701                 crit_exit();
702                 return (ENOMEM);
703         }
704
705         /*
706          * Fill in new entry
707          */
708         sqp->sq_next = NULL;
709         sqp->sq_cmd = cmd;
710         sqp->sq_func = func;
711         sqp->sq_token = token;
712         sqp->sq_arg1 = arg1;
713         sqp->sq_arg2 = arg2;
714         sqp->sq_connvc = cvp;
715
716         /*
717          * Put new entry at end of queue
718          */
719         if (atm_stackq_head == NULL)
720                 atm_stackq_head = sqp;
721         else
722                 atm_stackq_tail->sq_next = sqp;
723         atm_stackq_tail = sqp;
724
725         crit_exit();
726         return (0);
727 }
728
729
730 /*
731  * Drain the Stack Queue
732  * 
733  * Dequeues and processes entries from the global stack queue.  
734  *
735  * Arguments:
736  *      none
737  *
738  * Returns:
739  *      none
740  *
741  */
742 void
743 atm_stack_drain(void)
744 {
745         struct stackq_entry     *sqp, *qprev, *qnext;
746         int             cnt;
747
748         crit_enter();
749         /*
750          * Loop thru entire queue until queue is empty
751          *      (but panic rather loop forever)
752          */
753         do {
754                 cnt = 0;
755                 qprev = NULL;
756                 for (sqp = atm_stackq_head; sqp; ) {
757
758                         /*
759                          * Got an eligible entry, do STACK_CALL stuff
760                          */
761                         if (sqp->sq_cmd & STKCMD_UP) {
762                                 if (sqp->sq_connvc->cvc_downcnt) {
763
764                                         /*
765                                          * Cant process now, skip it
766                                          */
767                                         qprev = sqp;
768                                         sqp = sqp->sq_next;
769                                         continue;
770                                 }
771
772                                 /*
773                                  * OK, dispatch the call
774                                  */
775                                 sqp->sq_connvc->cvc_upcnt++;
776                                 (*sqp->sq_func)(sqp->sq_cmd, 
777                                                 sqp->sq_token,
778                                                 sqp->sq_arg1,
779                                                 sqp->sq_arg2);
780                                 sqp->sq_connvc->cvc_upcnt--;
781                         } else {
782                                 if (sqp->sq_connvc->cvc_upcnt) {
783
784                                         /*
785                                          * Cant process now, skip it
786                                          */
787                                         qprev = sqp;
788                                         sqp = sqp->sq_next;
789                                         continue;
790                                 }
791
792                                 /*
793                                  * OK, dispatch the call
794                                  */
795                                 sqp->sq_connvc->cvc_downcnt++;
796                                 (*sqp->sq_func)(sqp->sq_cmd, 
797                                                 sqp->sq_token,
798                                                 sqp->sq_arg1,
799                                                 sqp->sq_arg2);
800                                 sqp->sq_connvc->cvc_downcnt--;
801                         }
802
803                         /*
804                          * Dequeue processed entry and free it
805                          */
806                         cnt++;
807                         qnext = sqp->sq_next;
808                         if (qprev)
809                                 qprev->sq_next = qnext;
810                         else
811                                 atm_stackq_head = qnext;
812                         if (qnext == NULL)
813                                 atm_stackq_tail = qprev;
814                         atm_free((caddr_t)sqp);
815                         sqp = qnext;
816                 }
817         } while (cnt > 0);
818
819         /*
820          * Make sure entire queue was drained
821          */
822         if (atm_stackq_head != NULL)
823                 panic("atm_stack_drain: Queue not emptied");
824         crit_exit();
825 }
826
827
828 /*
829  * Process Interrupt Queue
830  * 
831  * Processes entries on the ATM interrupt queue.  This queue is used by
832  * device interface drivers in order to schedule events from the driver's 
833  * lower (interrupt) half to the driver's stack services.
834  *
835  * The interrupt routines must store the stack processing function to call
836  * and a token (typically a driver/stack control block) at the front of the
837  * queued buffer.  We assume that the function pointer and token values are 
838  * both contained (and properly aligned) in the first buffer of the chain.
839  *
840  * Arguments:
841  *      none
842  *
843  * Returns:
844  *      none
845  *
846  */
847 static void
848 atm_intr(netmsg_t msg)
849 {
850         struct mbuf *m = msg->packet.nm_packet;
851         caddr_t         cp;
852         atm_intr_func_t func;
853         void            *token;
854
855         /*
856          * Get function to call and token value
857          */
858         get_mplock();
859         KB_DATASTART(m, cp, caddr_t);
860         func = *(atm_intr_func_t *)cp;
861         cp += sizeof(func);
862         token = *(void **)cp;
863         KB_HEADADJ(m, -(sizeof(func) + sizeof(token)));
864         if (KB_LEN(m) == 0) {
865                 KBuffer         *m1;
866                 KB_UNLINKHEAD(m, m1);
867                 m = m1;
868         }
869
870         /*
871          * Call processing function
872          */
873         (*func)(token, m);
874
875         /*
876          * Drain any deferred calls
877          */
878         STACK_DRAIN();
879         rel_mplock();
880         /* msg was embedded in the mbuf, do not reply! */
881 }
882
883 /*
884  * Print a pdu buffer chain
885  * 
886  * Arguments:
887  *      m       pointer to pdu buffer chain
888  *      msg     pointer to message header string
889  *
890  * Returns:
891  *      none
892  *
893  */
894 void
895 atm_pdu_print(KBuffer *m, char *msg)
896 {
897         caddr_t         cp;
898         int             i;
899         char            c = ' ';
900
901         kprintf("%s:", msg);
902         while (m) { 
903                 KB_DATASTART(m, cp, caddr_t);
904                 kprintf("%cbfr=%p data=%p len=%d: ",
905                         c, m, cp, KB_LEN(m));
906                 c = '\t';
907                 if (atm_print_data) {
908                         for (i = 0; i < KB_LEN(m); i++) {
909                                 kprintf("%2x ", (u_char)*cp++);
910                         }
911                         kprintf("<end_bfr>\n");
912                 } else {
913                         kprintf("\n");
914                 }
915                 m = KB_NEXT(m);
916         }
917 }
918