]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i4b/driver/i4b_ing.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / sys / i4b / driver / i4b_ing.c
1 /*-
2  * Copyright (c) 1999, 2002 Hellmuth Michaelis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 /*---------------------------------------------------------------------------
27  *
28  *      i4b_ing.c - isdn4bsd B-channel to netgraph driver
29  *      -------------------------------------------------
30  *      last edit-date: [Sat Mar  9 14:09:53 2002]
31  *
32  *---------------------------------------------------------------------------*/ 
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include "opt_i4b.h"
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/mbuf.h>
43 #include <sys/socket.h>
44 #include <sys/errno.h>
45 #include <sys/malloc.h>
46
47 #include <net/if.h>
48
49 #include <netgraph/ng_message.h>
50 #include <netgraph/ng_parse.h>
51 #include <netgraph/netgraph.h>
52
53 #include <machine/i4b_ioctl.h>
54 #include <machine/i4b_debug.h>
55
56 #include <i4b/include/i4b_global.h>
57 #include <i4b/include/i4b_l3l4.h>
58
59 #include <i4b/layer4/i4b_l4.h>
60
61 #define I4BINGACCT      1               /* enable accounting messages */
62 #define I4BINGACCTINTVL 2               /* accounting msg interval in secs */
63
64 #define I4BINGMAXQLEN   50              /* max queue length */
65
66 /* initialized by L4 */
67
68 static drvr_link_t ing_drvr_linktab[NI4BING];
69 static isdn_link_t *isdn_linktab[NI4BING];
70
71 struct ing_softc {
72         int             sc_unit;        /* unit number                  */
73         int             sc_state;       /* state of the interface       */
74         call_desc_t     *sc_cdp;        /* ptr to call descriptor       */
75         int             sc_updown;      /* soft state of interface      */
76         struct ifqueue  sc_fastq;       /* interactive traffic          */
77         int             sc_dialresp;    /* dialresponse                 */
78         int             sc_lastdialresp;/* last dialresponse            */
79         
80 #if I4BINGACCT
81         struct callout_handle sc_callout;
82         int             sc_iinb;        /* isdn driver # of inbytes     */
83         int             sc_ioutb;       /* isdn driver # of outbytes    */
84         int             sc_inb;         /* # of bytes rx'd              */
85         int             sc_outb;        /* # of bytes tx'd              */
86         int             sc_linb;        /* last # of bytes rx'd         */
87         int             sc_loutb;       /* last # of bytes tx'd         */
88         int             sc_fn;          /* flag, first null acct        */
89 #endif  
90
91         int             sc_inpkt;       /* incoming packets             */
92         int             sc_outpkt;      /* outgoing packets             */      
93
94         struct ifqueue  xmitq_hipri;    /* hi-priority transmit queue */
95         struct ifqueue  xmitq;    /* transmit queue */
96                 
97         node_p          node;           /* back pointer to node */
98         char            nodename[NG_NODESIZ]; /* store our node name */
99         hook_p          debughook;
100         hook_p          hook;   
101
102         u_int           packets_in;     /* packets in from downstream */
103         u_int           packets_out;    /* packets out towards downstream */
104         u_int32_t       flags;
105
106 } ing_softc[NI4BING];
107
108 enum ing_states {
109         ST_IDLE,                        /* initialized, ready, idle     */
110         ST_DIALING,                     /* dialling out to remote       */
111         ST_CONNECTED                    /* connected to remote          */
112 };
113
114 static void i4bingattach(void *);
115
116 PSEUDO_SET(i4bingattach, i4b_ing);
117
118 static void ing_init_linktab(int unit);
119 static void ing_tx_queue_empty(int unit);
120
121 /* ========= NETGRAPH ============= */
122
123 #define NG_ING_NODE_TYPE        "i4bing"        /* node type name */
124 #define NGM_ING_COOKIE          947513046       /* node type cookie */
125
126 /* Hook names */
127 #define NG_ING_HOOK_DEBUG       "debug"
128 #define NG_ING_HOOK_RAW         "rawdata"
129
130 /* Netgraph commands understood by this node type */
131 enum {
132         NGM_ING_SET_FLAG = 1,
133         NGM_ING_GET_STATUS,
134 };
135
136 /* This structure is returned by the NGM_ING_GET_STATUS command */
137 struct ngingstat {
138         u_int   packets_in;     /* packets in from downstream */
139         u_int   packets_out;    /* packets out towards downstream */
140 };
141
142 /*
143  * This is used to define the 'parse type' for a struct ngingstat, which
144  * is bascially a description of how to convert a binary struct ngingstat
145  * to an ASCII string and back.  See ng_parse.h for more info.
146  *
147  * This needs to be kept in sync with the above structure definition
148  */
149 #define NG_ING_STATS_TYPE_INFO  {                               \
150           { "packets_in",       &ng_parse_int32_type    },      \
151           { "packets_out",      &ng_parse_int32_type    },      \
152           { NULL },                                             \
153 }
154
155 /*
156  * This section contains the netgraph method declarations for the
157  * sample node. These methods define the netgraph 'type'.
158  */
159
160 static ng_constructor_t ng_ing_constructor;
161 static ng_rcvmsg_t      ng_ing_rcvmsg;
162 static ng_shutdown_t    ng_ing_shutdown;
163 static ng_newhook_t     ng_ing_newhook;
164 static ng_connect_t     ng_ing_connect;
165 static ng_rcvdata_t     ng_ing_rcvdata;
166 static ng_disconnect_t  ng_ing_disconnect;
167
168 /* Parse type for struct ngingstat */
169 static const struct
170         ng_parse_struct_field ng_ing_stat_type_fields[] =
171         NG_ING_STATS_TYPE_INFO;
172
173 static const struct ng_parse_type ng_ing_stat_type = {
174         &ng_parse_struct_type,
175         &ng_ing_stat_type_fields
176 };
177
178 /* List of commands and how to convert arguments to/from ASCII */
179
180 static const struct ng_cmdlist ng_ing_cmdlist[] = {
181         {
182                 NGM_ING_COOKIE,
183                 NGM_ING_GET_STATUS,
184                 "getstatus",
185                 NULL,
186                 &ng_ing_stat_type,
187         },
188         {
189                 NGM_ING_COOKIE,
190                 NGM_ING_SET_FLAG,
191                 "setflag",
192                 &ng_parse_int32_type,
193                 NULL
194         },
195         { 0 }
196 };
197
198 /* Netgraph node type descriptor */
199 static struct ng_type typestruct = {
200         .version =      NG_ABI_VERSION,
201         .name =         NG_ING_NODE_TYPE,
202         .constructor =  ng_ing_constructor,
203         .rcvmsg =       ng_ing_rcvmsg,
204         .shutdown =     ng_ing_shutdown,
205         .newhook =      ng_ing_newhook,
206         .connect =      ng_ing_connect,
207         .rcvdata =      ng_ing_rcvdata,
208         .disconnect =   ng_ing_disconnect,
209         .cmdlist =      ng_ing_cmdlist,
210 };
211
212 NETGRAPH_INIT_ORDERED(ing, &typestruct, SI_SUB_DRIVERS, SI_ORDER_ANY);
213
214 /*===========================================================================*
215  *                      DEVICE DRIVER ROUTINES
216  *===========================================================================*/
217
218 /*---------------------------------------------------------------------------*
219  *      interface attach routine at kernel boot time
220  *---------------------------------------------------------------------------*/
221 static void
222 i4bingattach(void *dummy)
223 {
224         struct ing_softc *sc = ing_softc;
225         int i;
226         int ret;
227
228         printf("i4bing: %d i4b NetGraph ISDN B-channel device(s) attached\n", NI4BING);
229         
230         for(i=0; i < NI4BING; sc++, i++)
231         {
232                 sc->sc_unit = i;
233                 
234                 ing_init_linktab(i);
235
236                 NDBGL4(L4_DIALST, "setting dial state to ST_IDLE");
237
238                 sc->sc_state = ST_IDLE;
239                 
240                 sc->sc_fastq.ifq_maxlen = I4BINGMAXQLEN;
241                 if(!mtx_initialized(&sc->sc_fastq.ifq_mtx))
242                         mtx_init(&sc->sc_fastq.ifq_mtx, "i4b_ing_fastq", NULL, MTX_DEF);
243                 
244 #if I4BINGACCT
245                 callout_handle_init(&sc->sc_callout);
246                 sc->sc_iinb = 0;
247                 sc->sc_ioutb = 0;
248                 sc->sc_inb = 0;
249                 sc->sc_outb = 0;
250                 sc->sc_linb = 0;
251                 sc->sc_loutb = 0;
252                 sc->sc_fn = 1;
253 #endif
254
255                 sc->sc_inpkt = 0;
256                 sc->sc_outpkt = 0;              
257
258                 sc->sc_updown = SOFT_ENA;       /* soft enabled */
259
260                 sc->sc_dialresp = DSTAT_NONE;   /* no response */
261                 sc->sc_lastdialresp = DSTAT_NONE;
262                 
263                 /* setup a netgraph node */
264
265                 if ((ret = ng_make_node_common(&typestruct, &sc->node)))
266                 {
267                         printf("ing: ng_make_node_common, ret = %d\n!", ret);
268                 }
269
270                 /* name the netgraph node */
271
272                 sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit);
273                 if((ret = ng_name_node(sc->node, sc->nodename)))
274                 {
275                         printf("ing: ng_name node, ret = %d\n!", ret);
276                         NG_NODE_UNREF(sc->node);
277                         break;
278                 }
279
280                 NG_NODE_SET_PRIVATE(sc->node, sc);
281
282                 sc->xmitq.ifq_maxlen = IFQ_MAXLEN;
283                 sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN;
284                 if(!mtx_initialized(&sc->xmitq.ifq_mtx))
285                         mtx_init(&sc->xmitq.ifq_mtx, "i4b_ing_xmitq", NULL, MTX_DEF);
286                 if(!mtx_initialized(&sc->xmitq_hipri.ifq_mtx))
287                         mtx_init(&sc->xmitq_hipri.ifq_mtx, "i4b_ing_hipri", NULL, MTX_DEF);
288         }
289 }
290
291 #ifdef I4BINGACCT
292 /*---------------------------------------------------------------------------*
293  *      accounting timeout routine
294  *---------------------------------------------------------------------------*/
295 static void
296 ing_timeout(struct ing_softc *sc)
297 {
298         bchan_statistics_t bs;
299         int unit = sc->sc_unit;
300
301         /* get # of bytes in and out from the HSCX driver */ 
302         
303         (*isdn_linktab[unit]->bch_stat)
304                 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
305
306         sc->sc_ioutb += bs.outbytes;
307         sc->sc_iinb += bs.inbytes;
308         
309         if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn) 
310         {
311                 int ri = (sc->sc_iinb - sc->sc_linb)/I4BINGACCTINTVL;
312                 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BINGACCTINTVL;
313
314                 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
315                         sc->sc_fn = 0;
316                 else
317                         sc->sc_fn = 1;
318                         
319                 sc->sc_linb = sc->sc_iinb;
320                 sc->sc_loutb = sc->sc_ioutb;
321
322                 i4b_l4_accounting(BDRV_ING, unit, ACCT_DURING,
323                          sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
324         }
325
326         sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout,
327                                         (void *)sc, I4BINGACCTINTVL*hz);
328 }
329 #endif /* I4BINGACCT */
330
331 #if 0
332 /*---------------------------------------------------------------------------*
333  *      clear the interface's send queues
334  *---------------------------------------------------------------------------*/
335 static void
336 ingclearqueue(struct ifqueue *iq)
337 {
338         int x;
339         
340         x = splimp();
341         IF_DRAIN(iq);
342         splx(x);
343 }
344 #endif
345
346 /*===========================================================================*
347  *                      ISDN INTERFACE ROUTINES
348  *===========================================================================*/
349
350 /*---------------------------------------------------------------------------*
351  *      this routine is called from L4 handler at connect time
352  *---------------------------------------------------------------------------*/
353 static void
354 ing_connect(int unit, void *cdp)
355 {
356         struct ing_softc *sc = &ing_softc[unit];
357         int s;
358
359         sc->sc_cdp = (call_desc_t *)cdp;
360
361         s = SPLI4B();
362
363         NDBGL4(L4_DIALST, "ing%d: setting dial state to ST_CONNECTED", unit);
364
365         sc->sc_dialresp = DSTAT_NONE;
366         sc->sc_lastdialresp = DSTAT_NONE;       
367         
368 #if I4BINGACCT
369         sc->sc_iinb = 0;
370         sc->sc_ioutb = 0;
371         sc->sc_inb = 0;
372         sc->sc_outb = 0;
373         sc->sc_linb = 0;
374         sc->sc_loutb = 0;
375         sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout,
376                                 (void *)sc, I4BINGACCTINTVL*hz);
377 #endif
378
379         sc->sc_state = ST_CONNECTED;
380         
381         splx(s);
382 }
383         
384 /*---------------------------------------------------------------------------*
385  *      this routine is called from L4 handler at disconnect time
386  *---------------------------------------------------------------------------*/
387 static void
388 ing_disconnect(int unit, void *cdp)
389 {
390         call_desc_t *cd = (call_desc_t *)cdp;
391         struct ing_softc *sc = &ing_softc[unit];
392
393         /* new stuff to check that the active channel is being closed */
394
395         if (cd != sc->sc_cdp)
396         {
397                 NDBGL4(L4_INGDBG, "ing%d: channel %d not active",
398                                 cd->driver_unit, cd->channelid);
399                 return;
400         }
401
402 #if I4BINGACCT
403         untimeout((TIMEOUT_FUNC_T)ing_timeout,
404                 (void *)sc, sc->sc_callout);
405 #endif
406
407         i4b_l4_accounting(BDRV_ING, cd->driver_unit, ACCT_FINAL,
408                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb);
409         
410         sc->sc_cdp = (call_desc_t *)0;  
411
412         NDBGL4(L4_DIALST, "setting dial state to ST_IDLE");
413
414         sc->sc_dialresp = DSTAT_NONE;
415         sc->sc_lastdialresp = DSTAT_NONE;       
416
417         sc->sc_state = ST_IDLE;
418 }
419
420 /*---------------------------------------------------------------------------*
421  *      this routine is used to give a feedback from userland daemon
422  *      in case of dial problems
423  *---------------------------------------------------------------------------*/
424 static void
425 ing_dialresponse(int unit, int status, cause_t cause)
426 {
427         struct ing_softc *sc = &ing_softc[unit];
428         sc->sc_dialresp = status;
429
430         NDBGL4(L4_INGDBG, "ing%d: last=%d, this=%d",
431                 unit, sc->sc_lastdialresp, sc->sc_dialresp);
432
433         if(status != DSTAT_NONE)
434         {
435                 NDBGL4(L4_INGDBG, "ing%d: clearing queues", unit);
436 /*              ingclearqueues(sc); */
437         }
438 }
439         
440 /*---------------------------------------------------------------------------*
441  *      interface soft up/down
442  *---------------------------------------------------------------------------*/
443 static void
444 ing_updown(int unit, int updown)
445 {
446         struct ing_softc *sc = &ing_softc[unit];
447         sc->sc_updown = updown;
448 }
449         
450 /*---------------------------------------------------------------------------*
451  *      this routine is called from the HSCX interrupt handler
452  *      when a new frame (mbuf) has been received and was put on
453  *      the rx queue. It is assumed that this routines runs at
454  *      pri level splimp() ! Keep it short !
455  *---------------------------------------------------------------------------*/
456 static void
457 ing_rx_data_rdy(int unit)
458 {
459         register struct ing_softc *sc = &ing_softc[unit];
460         register struct mbuf *m;
461         int error;
462         
463         if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
464                 return;
465
466 #if I4BINGACCT
467         sc->sc_inb += m->m_pkthdr.len;
468 #endif
469
470         m->m_pkthdr.rcvif = NULL;
471
472         sc->sc_inpkt++;
473         
474         NG_SEND_DATA_ONLY(error, sc->hook, m);
475 }
476
477 /*---------------------------------------------------------------------------*
478  *      this routine is called from the HSCX interrupt handler
479  *      when the last frame has been sent out and there is no
480  *      further frame (mbuf) in the tx queue.
481  *---------------------------------------------------------------------------*/
482 static void
483 ing_tx_queue_empty(int unit)
484 {
485         register struct ing_softc *sc = &ing_softc[unit];
486         register struct mbuf *m;
487         int x = 0;
488
489         if(sc->sc_state != ST_CONNECTED)
490                 return;
491                 
492         for(;;)
493         {
494                 IF_DEQUEUE(&sc->xmitq_hipri, m);
495
496                 if(m == NULL)
497                 {
498                         IF_DEQUEUE(&sc->xmitq, m);
499                         if(m == NULL)
500                                 break;
501                 }
502         
503 #if I4BINGACCT
504                 sc->sc_outb += m->m_pkthdr.len;
505 #endif
506
507                 x = 1;
508
509                 if(! IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL))
510                 {
511                         NDBGL4(L4_INGDBG, "ing%d: tx queue full!", unit);
512                 }
513         }
514
515         if(x)
516                 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
517 }
518
519 /*---------------------------------------------------------------------------*
520  *      this routine is called from the HSCX interrupt handler
521  *      each time a packet is received or transmitted. It should
522  *      be used to implement an activity timeout mechanism.
523  *---------------------------------------------------------------------------*/
524 static void
525 ing_activity(int unit, int rxtx)
526 {
527         ing_softc[unit].sc_cdp->last_active_time = SECOND;
528 }
529
530 /*---------------------------------------------------------------------------*
531  *      return this drivers linktab address
532  *---------------------------------------------------------------------------*/
533 drvr_link_t *
534 ing_ret_linktab(int unit)
535 {
536         return(&ing_drvr_linktab[unit]);
537 }
538
539 /*---------------------------------------------------------------------------*
540  *      setup the isdn_linktab for this driver
541  *---------------------------------------------------------------------------*/
542 void
543 ing_set_linktab(int unit, isdn_link_t *ilt)
544 {
545         isdn_linktab[unit] = ilt;
546 }
547
548 /*---------------------------------------------------------------------------*
549  *      initialize this drivers linktab
550  *---------------------------------------------------------------------------*/
551 static void
552 ing_init_linktab(int unit)
553 {
554         ing_drvr_linktab[unit].unit = unit;
555         ing_drvr_linktab[unit].bch_rx_data_ready = ing_rx_data_rdy;
556         ing_drvr_linktab[unit].bch_tx_queue_empty = ing_tx_queue_empty;
557         ing_drvr_linktab[unit].bch_activity = ing_activity;
558         ing_drvr_linktab[unit].line_connected = ing_connect;
559         ing_drvr_linktab[unit].line_disconnected = ing_disconnect;
560         ing_drvr_linktab[unit].dial_response = ing_dialresponse;
561         ing_drvr_linktab[unit].updown_ind = ing_updown; 
562 }
563
564 /*===========================================================================*
565  *                      NETGRAPH INTERFACE ROUTINES
566  *===========================================================================*/
567
568 /*---------------------------------------------------------------------------*
569  * It is not possible or allowable to create a node of this type.
570  * If the hardware exists, it will already have created it.
571  *---------------------------------------------------------------------------*/
572 static int
573 ng_ing_constructor(node_p node)
574 {
575         return(EINVAL);
576 }
577
578 /*---------------------------------------------------------------------------*
579  * Give our ok for a hook to be added...
580  * Add the hook's private info to the hook structure.
581  *---------------------------------------------------------------------------*/
582 static int
583 ng_ing_newhook(node_p node, hook_p hook, const char *name)
584 {
585         struct ing_softc *sc = NG_NODE_PRIVATE(node);
586
587         /*
588          * check if it's our friend the debug hook
589          */
590         if(strcmp(name, NG_ING_HOOK_DEBUG) == 0)
591         {
592                 NG_HOOK_SET_PRIVATE(hook, NULL); /* paranoid */
593                 sc->debughook = hook;
594                 return (0);
595         }
596         /*
597          * Check for raw mode hook.
598          */
599         if(strcmp(name, NG_ING_HOOK_RAW) == 0)
600         {
601                 NG_HOOK_SET_PRIVATE(hook, sc);
602                 sc->hook = hook;
603                 return (0);
604         }
605
606         return (EINVAL);
607 }
608
609 /*---------------------------------------------------------------------------*
610  * Get a netgraph control message.
611  * Check it is one we understand. If needed, send a response.
612  * We could save the address for an async action later, but don't here.
613  * Always free the message.
614  * The response should be in a malloc'd region that the caller can 'free'.
615  * A response is not required.
616  *---------------------------------------------------------------------------*/
617 static int
618 ng_ing_rcvmsg(node_p node, item_p item, hook_p lasthook)
619 {
620         struct ing_softc *sc = NG_NODE_PRIVATE(node);
621
622         struct ng_mesg *resp = NULL;
623         int error = 0;
624         struct ng_mesg *msg;
625
626         NGI_GET_MSG(item, msg);
627
628         if(msg->header.typecookie == NGM_GENERIC_COOKIE)
629         {
630                 switch(msg->header.cmd)
631                 {
632                         case NGM_TEXT_STATUS:
633                         {
634                                 char *arg;
635                                 char *p;
636                                 int pos = 0;
637
638                                 NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT);
639
640                                 if (resp == NULL)
641                                 {
642                                         error = ENOMEM;
643                                         break;
644                                 }
645                                 arg = (char *) resp->data;
646
647                                 switch(sc->sc_state)
648                                 {
649                                         case ST_IDLE:
650                                                 p = "idle";
651                                                 break;
652                                         case ST_DIALING:
653                                                 p = "dialing";
654                                                 break;
655                                         case ST_CONNECTED:
656                                                 p = "connected";
657                                                 break;
658                                         default:
659                                                 p = "???";
660                                                 break;
661                                 }
662
663                                 pos = sprintf(arg, "state = %s (%d)\n", p, sc->sc_state);
664 #if I4BINGACCT
665                                 pos += sprintf(arg + pos, "%d bytes in, %d bytes out\n", sc->sc_inb, sc->sc_outb);
666 #endif                      
667                                 pos += sprintf(arg + pos, "%d pkts in, %d pkts out\n", sc->sc_inpkt, sc->sc_outpkt);
668
669                                 resp->header.arglen = pos + 1;
670                                 break;
671                         }
672
673                         default:
674                                 error = EINVAL;
675                                 break;
676                 }
677         }
678         else if(msg->header.typecookie == NGM_ING_COOKIE)
679         {
680                 switch (msg->header.cmd)
681                 {
682                         case NGM_ING_GET_STATUS:
683                         {
684                                 struct ngingstat *stats;
685
686                                 NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
687
688                                 if (!resp)
689                                 {
690                                         error = ENOMEM;
691                                         break;
692                                 }
693
694                                 stats = (struct ngingstat *) resp->data;
695                                 stats->packets_in = sc->packets_in;
696                                 stats->packets_out = sc->packets_out;
697                                 break;
698                         }
699
700                         case NGM_ING_SET_FLAG:
701                                 if (msg->header.arglen != sizeof(u_int32_t))
702                                 {
703                                         error = EINVAL;
704                                         break;
705                                 }
706                                 sc->flags = *((u_int32_t *) msg->data);
707                                 break;
708
709                         default:
710                                 error = EINVAL;         /* unknown command */
711                                 break;
712                 }
713         }
714         else
715         {
716                 error = EINVAL;                 /* unknown cookie type */
717         }
718
719         /* Take care of synchronous response, if any */
720         NG_RESPOND_MSG(error, node, item, resp);
721         /* Free the message and return */
722         NG_FREE_MSG(msg);
723         return(error);
724 }
725
726 /*---------------------------------------------------------------------------*
727  * get data from another node and transmit it out on a B-channel
728  *---------------------------------------------------------------------------*/
729 static int
730 ng_ing_rcvdata(hook_p hook, item_p item)
731 {
732         struct ing_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
733         struct ifqueue  *xmitq_p;
734         int s;
735         struct mbuf *m;
736         struct ng_tag_prio *ptag;
737         
738         NGI_GET_M(item, m);
739         NG_FREE_ITEM(item);
740
741         if(NG_HOOK_PRIVATE(hook) == NULL)
742         {
743                 NG_FREE_M(m);
744                 return(ENETDOWN);
745         }
746         
747         if(sc->sc_state == ST_IDLE || sc->sc_state == ST_DIALING)
748         {
749                 i4b_l4_dialout(BDRV_ING, sc->sc_unit);
750                 sc->sc_state = ST_DIALING;
751         }
752
753         sc->sc_outpkt++;
754         
755        /*
756         * Now queue the data for when it can be sent
757         */
758         if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE,
759             NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) )
760                 xmitq_p = (&sc->xmitq_hipri);
761         else
762                 xmitq_p = (&sc->xmitq);
763
764         s = splimp();
765
766         IF_LOCK(xmitq_p);
767         if (_IF_QFULL(xmitq_p))
768         {
769                 _IF_DROP(xmitq_p);
770                 IF_UNLOCK(xmitq_p);
771                 splx(s);
772                 NG_FREE_M(m);
773                 return(ENOBUFS);
774         }
775
776         _IF_ENQUEUE(xmitq_p, m);
777         IF_UNLOCK(xmitq_p);
778
779         ing_tx_queue_empty(sc->sc_unit);
780
781         splx(s);
782         return (0);
783 }
784
785 /*---------------------------------------------------------------------------*
786  * Do local shutdown processing..
787  * If we are a persistant device, we might refuse to go away, and
788  * we'd only remove our links and reset ourself.
789  *---------------------------------------------------------------------------*/
790 static int
791 ng_ing_shutdown(node_p node)
792 {
793         struct ing_softc *sc = NG_NODE_PRIVATE(node);
794         int     ret;
795
796         NG_NODE_UNREF(node);
797
798         sc->packets_in = 0;             /* reset stats */
799         sc->packets_out = 0;
800
801         if ((ret = ng_make_node_common(&typestruct, &sc->node)))
802         {
803                 printf("ing: ng_make_node_common, ret = %d\n!", ret);
804         }
805
806         /* name the netgraph node */
807         sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit);
808         if((ret = ng_name_node(sc->node, sc->nodename)))
809         {
810                 printf("ing: ng_name node, ret = %d\n!", ret);
811                 NG_NODE_UNREF(sc->node);
812                 return (0);
813         }
814
815         NG_NODE_SET_PRIVATE(sc->node, sc);
816
817         return (0);
818 }
819
820 /*---------------------------------------------------------------------------*
821  * This is called once we've already connected a new hook to the other node.
822  *---------------------------------------------------------------------------*/
823 static int
824 ng_ing_connect(hook_p hook)
825 {
826         /* probably not at splnet, force outward queueing */
827         NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
828         return (0);
829 }
830
831 /*
832  * Dook disconnection
833  *
834  * For this type, removal of the last link destroys the node
835  */
836 static int
837 ng_ing_disconnect(hook_p hook)
838 {
839         struct ing_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
840         int s;
841         
842         if(NG_HOOK_PRIVATE(hook))
843         {
844                 s = splimp();
845                 splx(s);
846         }
847         else
848         {
849                 sc->debughook = NULL;
850         }
851         return (0);
852 }
853
854 /*===========================================================================*/