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