Remove not needed void casts.
[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.15 2004/09/16 22:59:06 joerg 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 static struct callout atm_timexp_ch;
71
72 /*
73  * Local functions
74  */
75 static void     atm_compact (struct atm_time *);
76 static KTimeout_ret     atm_timexp (void *);
77 static int      atm_intr(struct netmsg *);
78
79 /*
80  * Local variables
81  */
82 static struct atm_time  *atm_timeq = NULL;
83 static struct atm_time  atm_compactimer = {0, 0};
84
85 static struct sp_info   atm_stackq_pool = {
86         "Service stack queue pool",     /* si_name */
87         sizeof(struct stackq_entry),    /* si_blksiz */
88         10,                             /* si_blkcnt */
89         10                              /* si_maxallow */
90 };
91
92
93 /*
94  * Initialize ATM kernel
95  * 
96  * Performs any initialization required before things really get underway.
97  * Called from ATM domain initialization or from first registration function 
98  * which gets called.
99  *
100  * Arguments:
101  *      none
102  *
103  * Returns:
104  *      none
105  *
106  */
107 void
108 atm_initialize()
109 {
110         /*
111          * Never called from interrupts, so no locking needed
112          */
113         if (atm_init)
114                 return;
115         atm_init = 1;
116
117 #ifndef __DragonFly__
118         /*
119          * Add ATM protocol family
120          */
121         (void) protocol_family(&atmdomain, NULL, NULL);
122 #endif
123
124         atm_intrq.ifq_maxlen = ATM_INTRQ_MAX;
125 #ifdef sgi
126         atm_intr_index = register_isr(atm_intr);
127 #endif
128 #ifdef __DragonFly__
129         netisr_register(NETISR_ATM, cpu0_portfn, atm_intr);
130 #endif
131
132         /*
133          * Initialize subsystems
134          */
135         atm_aal5_init();
136
137         /*
138          * Prime the timer
139          */
140         callout_init(&atm_timexp_ch);
141         callout_reset(&atm_timexp_ch, hz / ATM_HZ, atm_timexp, NULL);
142
143         /*
144          * Start the compaction timer
145          */
146         atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
147 }
148
149
150 /*
151  * Allocate a Control Block
152  * 
153  * Gets a new control block allocated from the specified storage pool, 
154  * acquiring memory for new pool chunks if required.  The returned control
155  * block's contents will be cleared.
156  *
157  * Arguments:
158  *      sip     pointer to sp_info for storage pool
159  *
160  * Returns:
161  *      addr    pointer to allocated control block
162  *      0       allocation failed
163  *
164  */
165 void *
166 atm_allocate(sip)
167         struct sp_info  *sip;
168 {
169         void            *bp;
170         struct sp_chunk *scp;
171         struct sp_link  *slp;
172         int             s = splnet();
173
174         /*
175          * Count calls
176          */
177         sip->si_allocs++;
178
179         /*
180          * Are there any free in the pool?
181          */
182         if (sip->si_free) {
183
184                 /*
185                  * Find first chunk with a free block
186                  */
187                 for (scp = sip->si_poolh; scp; scp = scp->sc_next) {
188                         if (scp->sc_freeh != NULL)
189                                 break;
190                 }
191
192         } else {
193
194                 /*
195                  * No free blocks - have to allocate a new
196                  * chunk (but put a limit to this)
197                  */
198                 struct sp_link  *slp_next;
199                 int     i;
200
201                 /*
202                  * First time for this pool??
203                  */
204                 if (sip->si_chunksiz == 0) {
205                         size_t  n;
206
207                         /*
208                          * Initialize pool information
209                          */
210                         n = sizeof(struct sp_chunk) +
211                                 sip->si_blkcnt * 
212                                 (sip->si_blksiz + sizeof(struct sp_link));
213                         sip->si_chunksiz = roundup(n, SPOOL_ROUNDUP);
214
215                         /*
216                          * Place pool on kernel chain
217                          */
218                         LINK2TAIL(sip, struct sp_info, atm_pool_head, si_next);
219                 }
220
221                 if (sip->si_chunks >= sip->si_maxallow) {
222                         sip->si_fails++;
223                         (void) splx(s);
224                         return (NULL);
225                 }
226
227                 scp = KM_ALLOC(sip->si_chunksiz, M_DEVBUF,
228                                 M_INTWAIT | M_NULLOK);
229                 if (scp == NULL) {
230                         sip->si_fails++;
231                         (void) splx(s);
232                         return (NULL);
233                 }
234                 scp->sc_next = NULL;
235                 scp->sc_info = sip;
236                 scp->sc_magic = SPOOL_MAGIC;
237                 scp->sc_used = 0;
238
239                 /*
240                  * Divy up chunk into free blocks
241                  */
242                 slp = (struct sp_link *)(scp + 1);
243                 scp->sc_freeh = slp;
244
245                 for (i = sip->si_blkcnt; i > 1; i--) { 
246                         slp_next = (struct sp_link *)((caddr_t)(slp + 1) + 
247                                         sip->si_blksiz);
248                         slp->sl_u.slu_next = slp_next;
249                         slp = slp_next;
250                 }
251                 slp->sl_u.slu_next = NULL;
252                 scp->sc_freet = slp;
253
254                 /*
255                  * Add new chunk to end of pool
256                  */
257                 if (sip->si_poolh)
258                         sip->si_poolt->sc_next = scp;
259                 else
260                         sip->si_poolh = scp;
261                 sip->si_poolt = scp;
262                 
263                 sip->si_chunks++;
264                 sip->si_total += sip->si_blkcnt;
265                 sip->si_free += sip->si_blkcnt;
266                 if (sip->si_chunks > sip->si_maxused)
267                         sip->si_maxused = sip->si_chunks;
268         }
269
270         /*
271          * Allocate the first free block in chunk
272          */
273         slp = scp->sc_freeh;
274         scp->sc_freeh = slp->sl_u.slu_next;
275         scp->sc_used++;
276         sip->si_free--;
277         bp = (slp + 1);
278
279         /*
280          * Save link back to pool chunk
281          */
282         slp->sl_u.slu_chunk = scp;
283
284         /*
285          * Clear out block
286          */
287         KM_ZERO(bp, sip->si_blksiz);
288
289         (void) splx(s);
290         return (bp);
291 }
292
293
294 /*
295  * Free a Control Block
296  * 
297  * Returns a previously allocated control block back to the owners 
298  * storage pool.  
299  *
300  * Arguments:
301  *      bp      pointer to block to be freed
302  *
303  * Returns:
304  *      none
305  *
306  */
307 void
308 atm_free(bp)
309         void            *bp;
310 {
311         struct sp_info  *sip;
312         struct sp_chunk *scp;
313         struct sp_link  *slp;
314         int             s = splnet();
315
316         /*
317          * Get containing chunk and pool info
318          */
319         slp = (struct sp_link *)bp;
320         slp--;
321         scp = slp->sl_u.slu_chunk;
322         if (scp->sc_magic != SPOOL_MAGIC)
323                 panic("atm_free: chunk magic missing");
324         sip = scp->sc_info;
325
326         /*
327          * Add block to free chain
328          */
329         if (scp->sc_freeh) {
330                 scp->sc_freet->sl_u.slu_next = slp;
331                 scp->sc_freet = slp;
332         } else
333                 scp->sc_freeh = scp->sc_freet = slp;
334         slp->sl_u.slu_next = NULL;
335         sip->si_free++;
336         scp->sc_used--;
337
338         (void) splx(s);
339         return;
340 }
341
342
343 /*
344  * Storage Pool Compaction
345  * 
346  * Called periodically in order to perform compaction of the
347  * storage pools.  Each pool will be checked to see if any chunks 
348  * can be freed, taking some care to avoid freeing too many chunks
349  * in order to avoid memory thrashing.
350  *
351  * Called at splnet.
352  *
353  * Arguments:
354  *      tip     pointer to timer control block (atm_compactimer)
355  *
356  * Returns:
357  *      none
358  *
359  */
360 static void
361 atm_compact(tip)
362         struct atm_time *tip;
363 {
364         struct sp_info  *sip;
365         struct sp_chunk *scp;
366         int             i;
367         struct sp_chunk *scp_prev;
368
369         /*
370          * Check out all storage pools
371          */
372         for (sip = atm_pool_head; sip; sip = sip->si_next) {
373
374                 /*
375                  * Always keep a minimum number of chunks around
376                  */
377                 if (sip->si_chunks <= SPOOL_MIN_CHUNK)
378                         continue;
379
380                 /*
381                  * Maximum chunks to free at one time will leave
382                  * pool with at least 50% utilization, but never
383                  * go below minimum chunk count.
384                  */
385                 i = ((sip->si_free * 2) - sip->si_total) / sip->si_blkcnt;
386                 i = MIN(i, sip->si_chunks - SPOOL_MIN_CHUNK);
387
388                 /*
389                  * Look for chunks to free
390                  */
391                 scp_prev = NULL;
392                 for (scp = sip->si_poolh; scp && i > 0; ) {
393
394                         if (scp->sc_used == 0) {
395
396                                 /*
397                                  * Found a chunk to free, so do it
398                                  */
399                                 if (scp_prev) {
400                                         scp_prev->sc_next = scp->sc_next;
401                                         if (sip->si_poolt == scp)
402                                                 sip->si_poolt = scp_prev;
403                                 } else
404                                         sip->si_poolh = scp->sc_next;
405
406                                 KM_FREE((caddr_t)scp, sip->si_chunksiz,
407                                         M_DEVBUF);
408
409                                 /*
410                                  * Update pool controls
411                                  */
412                                 sip->si_chunks--;
413                                 sip->si_total -= sip->si_blkcnt;
414                                 sip->si_free -= sip->si_blkcnt;
415                                 i--;
416                                 if (scp_prev)
417                                         scp = scp_prev->sc_next;
418                                 else
419                                         scp = sip->si_poolh;
420                         } else {
421                                 scp_prev = scp;
422                                 scp = scp->sc_next;
423                         }
424                 }
425         }
426
427         /*
428          * Restart the compaction timer
429          */
430         atm_timeout(&atm_compactimer, SPOOL_COMPACT, atm_compact);
431
432         return;
433 }
434
435
436 /*
437  * Release a Storage Pool
438  * 
439  * Frees all dynamic storage acquired for a storage pool.
440  * This function is normally called just prior to a module's unloading.
441  *
442  * Arguments:
443  *      sip     pointer to sp_info for storage pool
444  *
445  * Returns:
446  *      none
447  *
448  */
449 void
450 atm_release_pool(sip)
451         struct sp_info  *sip;
452 {
453         struct sp_chunk *scp, *scp_next;
454         int             s = splnet();
455
456         /*
457          * Free each chunk in pool
458          */
459         for (scp = sip->si_poolh; scp; scp = scp_next) {
460
461                 /*
462                  * Check for memory leaks
463                  */
464                 if (scp->sc_used)
465                         panic("atm_release_pool: unfreed blocks");
466
467                 scp_next = scp->sc_next;
468
469                 KM_FREE((caddr_t)scp, sip->si_chunksiz, M_DEVBUF);
470         }
471
472         /*
473          * Update pool controls
474          */
475         sip->si_poolh = NULL;
476         sip->si_chunks = 0;
477         sip->si_total = 0;
478         sip->si_free = 0;
479
480         /*
481          * Unlink pool from active chain
482          */
483         sip->si_chunksiz = 0;
484         UNLINK(sip, struct sp_info, atm_pool_head, si_next);
485
486         (void) splx(s);
487         return;
488 }
489
490
491 /*
492  * Handle timer tick expiration
493  * 
494  * Decrement tick count in first block on timer queue.  If there
495  * are blocks with expired timers, call their timeout function.
496  * This function is called ATM_HZ times per second.
497  *
498  * Arguments:
499  *      arg     argument passed on timeout() call
500  *
501  * Returns:
502  *      none
503  *
504  */
505 static KTimeout_ret
506 atm_timexp(arg)
507         void    *arg;
508 {
509         struct atm_time *tip;
510         int             s = splimp();
511
512
513         /*
514          * Decrement tick count
515          */
516         if (((tip = atm_timeq) == NULL) || (--tip->ti_ticks > 0)) {
517                 goto restart;
518         }
519
520         /*
521          * Stack queue should have been drained
522          */
523 #ifdef DIAGNOSTIC
524         if (atm_stackq_head != NULL)
525                 panic("atm_timexp: stack queue not empty");
526 #endif
527
528         /*
529          * Dispatch expired timers
530          */
531         while (((tip = atm_timeq) != NULL) && (tip->ti_ticks == 0)) {
532                 void    (*func)(struct atm_time *);
533
534                 /*
535                  * Remove expired block from queue
536                  */
537                 atm_timeq = tip->ti_next;
538                 tip->ti_flag &= ~TIF_QUEUED;
539
540                 /*
541                  * Call timeout handler (with network interrupts locked out)
542                  */
543                 func = tip->ti_func;
544                 (void) splx(s);
545                 s = splnet();
546                 (*func)(tip);
547                 (void) splx(s);
548                 s = splimp();
549
550                 /*
551                  * Drain any deferred calls
552                  */
553                 STACK_DRAIN();
554         }
555
556 restart:
557         /*
558          * Restart the timer
559          */
560         (void) splx(s);
561         callout_reset(&atm_timexp_ch, hz / ATM_HZ, atm_timexp, NULL);
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 int
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         return(EASYNC);
918 }
919
920 /*
921  * Print a pdu buffer chain
922  * 
923  * Arguments:
924  *      m       pointer to pdu buffer chain
925  *      msg     pointer to message header string
926  *
927  * Returns:
928  *      none
929  *
930  */
931 void
932 atm_pdu_print(m, msg)
933         KBuffer         *m;
934         char            *msg;
935 {
936         caddr_t         cp;
937         int             i;
938         char            c = ' ';
939
940         printf("%s:", msg);
941         while (m) { 
942                 KB_DATASTART(m, cp, caddr_t);
943                 printf("%cbfr=%p data=%p len=%d: ",
944                         c, m, cp, KB_LEN(m));
945                 c = '\t';
946                 if (atm_print_data) {
947                         for (i = 0; i < KB_LEN(m); i++) {
948                                 printf("%2x ", (u_char)*cp++);
949                         }
950                         printf("<end_bfr>\n");
951                 } else {
952                         printf("\n");
953                 }
954                 m = KB_NEXT(m);
955         }
956 }
957