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