2 * Copyright (c) 1999, 2002 Hellmuth Michaelis. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 /*---------------------------------------------------------------------------
28 * i4b_ing.c - isdn4bsd B-channel to netgraph driver
29 * -------------------------------------------------
30 * last edit-date: [Sat Mar 9 14:09:53 2002]
32 *---------------------------------------------------------------------------*/
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
43 #include <sys/socket.h>
44 #include <sys/errno.h>
45 #include <sys/malloc.h>
49 #include <netgraph/ng_message.h>
50 #include <netgraph/ng_parse.h>
51 #include <netgraph/netgraph.h>
53 #include <i4b/include/i4b_ioctl.h>
54 #include <i4b/include/i4b_debug.h>
56 #include <i4b/include/i4b_global.h>
57 #include <i4b/include/i4b_l3l4.h>
59 #include <i4b/layer4/i4b_l4.h>
61 #error "Cannot be used until I4B is locked."
63 #define I4BINGACCT 1 /* enable accounting messages */
64 #define I4BINGACCTINTVL 2 /* accounting msg interval in secs */
66 #define I4BINGMAXQLEN 50 /* max queue length */
68 /* initialized by L4 */
70 static drvr_link_t ing_drvr_linktab[NI4BING];
71 static isdn_link_t *isdn_linktab[NI4BING];
74 int sc_unit; /* unit number */
75 int sc_state; /* state of the interface */
76 call_desc_t *sc_cdp; /* ptr to call descriptor */
77 int sc_updown; /* soft state of interface */
78 struct ifqueue sc_fastq; /* interactive traffic */
79 int sc_dialresp; /* dialresponse */
80 int sc_lastdialresp;/* last dialresponse */
83 struct callout_handle sc_callout;
84 int sc_iinb; /* isdn driver # of inbytes */
85 int sc_ioutb; /* isdn driver # of outbytes */
86 int sc_inb; /* # of bytes rx'd */
87 int sc_outb; /* # of bytes tx'd */
88 int sc_linb; /* last # of bytes rx'd */
89 int sc_loutb; /* last # of bytes tx'd */
90 int sc_fn; /* flag, first null acct */
93 int sc_inpkt; /* incoming packets */
94 int sc_outpkt; /* outgoing packets */
96 struct ifqueue xmitq_hipri; /* hi-priority transmit queue */
97 struct ifqueue xmitq; /* transmit queue */
99 node_p node; /* back pointer to node */
100 char nodename[NG_NODESIZ]; /* store our node name */
104 u_int packets_in; /* packets in from downstream */
105 u_int packets_out; /* packets out towards downstream */
108 } ing_softc[NI4BING];
111 ST_IDLE, /* initialized, ready, idle */
112 ST_DIALING, /* dialling out to remote */
113 ST_CONNECTED /* connected to remote */
116 static void i4bingattach(void *);
118 PSEUDO_SET(i4bingattach, i4b_ing);
120 static void ing_init_linktab(int unit);
121 static void ing_tx_queue_empty(int unit);
123 /* ========= NETGRAPH ============= */
125 #define NG_ING_NODE_TYPE "i4bing" /* node type name */
126 #define NGM_ING_COOKIE 947513046 /* node type cookie */
129 #define NG_ING_HOOK_DEBUG "debug"
130 #define NG_ING_HOOK_RAW "rawdata"
132 /* Netgraph commands understood by this node type */
134 NGM_ING_SET_FLAG = 1,
138 /* This structure is returned by the NGM_ING_GET_STATUS command */
140 u_int packets_in; /* packets in from downstream */
141 u_int packets_out; /* packets out towards downstream */
145 * This is used to define the 'parse type' for a struct ngingstat, which
146 * is bascially a description of how to convert a binary struct ngingstat
147 * to an ASCII string and back. See ng_parse.h for more info.
149 * This needs to be kept in sync with the above structure definition
151 #define NG_ING_STATS_TYPE_INFO { \
152 { "packets_in", &ng_parse_int32_type }, \
153 { "packets_out", &ng_parse_int32_type }, \
158 * This section contains the netgraph method declarations for the
159 * sample node. These methods define the netgraph 'type'.
162 static ng_constructor_t ng_ing_constructor;
163 static ng_rcvmsg_t ng_ing_rcvmsg;
164 static ng_shutdown_t ng_ing_shutdown;
165 static ng_newhook_t ng_ing_newhook;
166 static ng_connect_t ng_ing_connect;
167 static ng_rcvdata_t ng_ing_rcvdata;
168 static ng_disconnect_t ng_ing_disconnect;
170 /* Parse type for struct ngingstat */
172 ng_parse_struct_field ng_ing_stat_type_fields[] =
173 NG_ING_STATS_TYPE_INFO;
175 static const struct ng_parse_type ng_ing_stat_type = {
176 &ng_parse_struct_type,
177 &ng_ing_stat_type_fields
180 /* List of commands and how to convert arguments to/from ASCII */
182 static const struct ng_cmdlist ng_ing_cmdlist[] = {
194 &ng_parse_int32_type,
200 /* Netgraph node type descriptor */
201 static struct ng_type typestruct = {
202 .version = NG_ABI_VERSION,
203 .name = NG_ING_NODE_TYPE,
204 .constructor = ng_ing_constructor,
205 .rcvmsg = ng_ing_rcvmsg,
206 .shutdown = ng_ing_shutdown,
207 .newhook = ng_ing_newhook,
208 .connect = ng_ing_connect,
209 .rcvdata = ng_ing_rcvdata,
210 .disconnect = ng_ing_disconnect,
211 .cmdlist = ng_ing_cmdlist,
214 NETGRAPH_INIT_ORDERED(ing, &typestruct, SI_SUB_DRIVERS, SI_ORDER_ANY);
216 /*===========================================================================*
217 * DEVICE DRIVER ROUTINES
218 *===========================================================================*/
220 /*---------------------------------------------------------------------------*
221 * interface attach routine at kernel boot time
222 *---------------------------------------------------------------------------*/
224 i4bingattach(void *dummy)
226 struct ing_softc *sc = ing_softc;
230 printf("i4bing: %d i4b NetGraph ISDN B-channel device(s) attached\n", NI4BING);
232 for(i=0; i < NI4BING; sc++, i++)
238 NDBGL4(L4_DIALST, "setting dial state to ST_IDLE");
240 sc->sc_state = ST_IDLE;
242 sc->sc_fastq.ifq_maxlen = I4BINGMAXQLEN;
243 if(!mtx_initialized(&sc->sc_fastq.ifq_mtx))
244 mtx_init(&sc->sc_fastq.ifq_mtx, "i4b_ing_fastq", NULL, MTX_DEF);
247 callout_handle_init(&sc->sc_callout);
260 sc->sc_updown = SOFT_ENA; /* soft enabled */
262 sc->sc_dialresp = DSTAT_NONE; /* no response */
263 sc->sc_lastdialresp = DSTAT_NONE;
265 /* setup a netgraph node */
267 if ((ret = ng_make_node_common(&typestruct, &sc->node)))
269 printf("ing: ng_make_node_common, ret = %d\n!", ret);
272 /* name the netgraph node */
274 sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit);
275 if((ret = ng_name_node(sc->node, sc->nodename)))
277 printf("ing: ng_name node, ret = %d\n!", ret);
278 NG_NODE_UNREF(sc->node);
282 NG_NODE_SET_PRIVATE(sc->node, sc);
284 sc->xmitq.ifq_maxlen = IFQ_MAXLEN;
285 sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN;
286 if(!mtx_initialized(&sc->xmitq.ifq_mtx))
287 mtx_init(&sc->xmitq.ifq_mtx, "i4b_ing_xmitq", NULL, MTX_DEF);
288 if(!mtx_initialized(&sc->xmitq_hipri.ifq_mtx))
289 mtx_init(&sc->xmitq_hipri.ifq_mtx, "i4b_ing_hipri", NULL, MTX_DEF);
294 /*---------------------------------------------------------------------------*
295 * accounting timeout routine
296 *---------------------------------------------------------------------------*/
298 ing_timeout(struct ing_softc *sc)
300 bchan_statistics_t bs;
301 int unit = sc->sc_unit;
303 /* get # of bytes in and out from the HSCX driver */
305 (*isdn_linktab[unit]->bch_stat)
306 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
308 sc->sc_ioutb += bs.outbytes;
309 sc->sc_iinb += bs.inbytes;
311 if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn)
313 int ri = (sc->sc_iinb - sc->sc_linb)/I4BINGACCTINTVL;
314 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BINGACCTINTVL;
316 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
321 sc->sc_linb = sc->sc_iinb;
322 sc->sc_loutb = sc->sc_ioutb;
324 i4b_l4_accounting(BDRV_ING, unit, ACCT_DURING,
325 sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
328 sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout,
329 (void *)sc, I4BINGACCTINTVL*hz);
331 #endif /* I4BINGACCT */
334 /*---------------------------------------------------------------------------*
335 * clear the interface's send queues
336 *---------------------------------------------------------------------------*/
338 ingclearqueue(struct ifqueue *iq)
348 /*===========================================================================*
349 * ISDN INTERFACE ROUTINES
350 *===========================================================================*/
352 /*---------------------------------------------------------------------------*
353 * this routine is called from L4 handler at connect time
354 *---------------------------------------------------------------------------*/
356 ing_connect(int unit, void *cdp)
358 struct ing_softc *sc = &ing_softc[unit];
361 sc->sc_cdp = (call_desc_t *)cdp;
365 NDBGL4(L4_DIALST, "ing%d: setting dial state to ST_CONNECTED", unit);
367 sc->sc_dialresp = DSTAT_NONE;
368 sc->sc_lastdialresp = DSTAT_NONE;
377 sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout,
378 (void *)sc, I4BINGACCTINTVL*hz);
381 sc->sc_state = ST_CONNECTED;
386 /*---------------------------------------------------------------------------*
387 * this routine is called from L4 handler at disconnect time
388 *---------------------------------------------------------------------------*/
390 ing_disconnect(int unit, void *cdp)
392 call_desc_t *cd = (call_desc_t *)cdp;
393 struct ing_softc *sc = &ing_softc[unit];
395 /* new stuff to check that the active channel is being closed */
397 if (cd != sc->sc_cdp)
399 NDBGL4(L4_INGDBG, "ing%d: channel %d not active",
400 cd->driver_unit, cd->channelid);
405 untimeout((TIMEOUT_FUNC_T)ing_timeout,
406 (void *)sc, sc->sc_callout);
409 i4b_l4_accounting(BDRV_ING, cd->driver_unit, ACCT_FINAL,
410 sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb);
412 sc->sc_cdp = (call_desc_t *)0;
414 NDBGL4(L4_DIALST, "setting dial state to ST_IDLE");
416 sc->sc_dialresp = DSTAT_NONE;
417 sc->sc_lastdialresp = DSTAT_NONE;
419 sc->sc_state = ST_IDLE;
422 /*---------------------------------------------------------------------------*
423 * this routine is used to give a feedback from userland daemon
424 * in case of dial problems
425 *---------------------------------------------------------------------------*/
427 ing_dialresponse(int unit, int status, cause_t cause)
429 struct ing_softc *sc = &ing_softc[unit];
430 sc->sc_dialresp = status;
432 NDBGL4(L4_INGDBG, "ing%d: last=%d, this=%d",
433 unit, sc->sc_lastdialresp, sc->sc_dialresp);
435 if(status != DSTAT_NONE)
437 NDBGL4(L4_INGDBG, "ing%d: clearing queues", unit);
438 /* ingclearqueues(sc); */
442 /*---------------------------------------------------------------------------*
443 * interface soft up/down
444 *---------------------------------------------------------------------------*/
446 ing_updown(int unit, int updown)
448 struct ing_softc *sc = &ing_softc[unit];
449 sc->sc_updown = updown;
452 /*---------------------------------------------------------------------------*
453 * this routine is called from the HSCX interrupt handler
454 * when a new frame (mbuf) has been received and was put on
455 * the rx queue. It is assumed that this routines runs at
456 * pri level splimp() ! Keep it short !
457 *---------------------------------------------------------------------------*/
459 ing_rx_data_rdy(int unit)
461 register struct ing_softc *sc = &ing_softc[unit];
462 register struct mbuf *m;
465 if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
469 sc->sc_inb += m->m_pkthdr.len;
472 m->m_pkthdr.rcvif = NULL;
476 NG_SEND_DATA_ONLY(error, sc->hook, m);
479 /*---------------------------------------------------------------------------*
480 * this routine is called from the HSCX interrupt handler
481 * when the last frame has been sent out and there is no
482 * further frame (mbuf) in the tx queue.
483 *---------------------------------------------------------------------------*/
485 ing_tx_queue_empty(int unit)
487 register struct ing_softc *sc = &ing_softc[unit];
488 register struct mbuf *m;
491 if(sc->sc_state != ST_CONNECTED)
496 IF_DEQUEUE(&sc->xmitq_hipri, m);
500 IF_DEQUEUE(&sc->xmitq, m);
506 sc->sc_outb += m->m_pkthdr.len;
511 if(! IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL))
513 NDBGL4(L4_INGDBG, "ing%d: tx queue full!", unit);
518 (*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
521 /*---------------------------------------------------------------------------*
522 * this routine is called from the HSCX interrupt handler
523 * each time a packet is received or transmitted. It should
524 * be used to implement an activity timeout mechanism.
525 *---------------------------------------------------------------------------*/
527 ing_activity(int unit, int rxtx)
529 ing_softc[unit].sc_cdp->last_active_time = SECOND;
532 /*---------------------------------------------------------------------------*
533 * return this drivers linktab address
534 *---------------------------------------------------------------------------*/
536 ing_ret_linktab(int unit)
538 return(&ing_drvr_linktab[unit]);
541 /*---------------------------------------------------------------------------*
542 * setup the isdn_linktab for this driver
543 *---------------------------------------------------------------------------*/
545 ing_set_linktab(int unit, isdn_link_t *ilt)
547 isdn_linktab[unit] = ilt;
550 /*---------------------------------------------------------------------------*
551 * initialize this drivers linktab
552 *---------------------------------------------------------------------------*/
554 ing_init_linktab(int unit)
556 ing_drvr_linktab[unit].unit = unit;
557 ing_drvr_linktab[unit].bch_rx_data_ready = ing_rx_data_rdy;
558 ing_drvr_linktab[unit].bch_tx_queue_empty = ing_tx_queue_empty;
559 ing_drvr_linktab[unit].bch_activity = ing_activity;
560 ing_drvr_linktab[unit].line_connected = ing_connect;
561 ing_drvr_linktab[unit].line_disconnected = ing_disconnect;
562 ing_drvr_linktab[unit].dial_response = ing_dialresponse;
563 ing_drvr_linktab[unit].updown_ind = ing_updown;
566 /*===========================================================================*
567 * NETGRAPH INTERFACE ROUTINES
568 *===========================================================================*/
570 /*---------------------------------------------------------------------------*
571 * It is not possible or allowable to create a node of this type.
572 * If the hardware exists, it will already have created it.
573 *---------------------------------------------------------------------------*/
575 ng_ing_constructor(node_p node)
580 /*---------------------------------------------------------------------------*
581 * Give our ok for a hook to be added...
582 * Add the hook's private info to the hook structure.
583 *---------------------------------------------------------------------------*/
585 ng_ing_newhook(node_p node, hook_p hook, const char *name)
587 struct ing_softc *sc = NG_NODE_PRIVATE(node);
590 * check if it's our friend the debug hook
592 if(strcmp(name, NG_ING_HOOK_DEBUG) == 0)
594 NG_HOOK_SET_PRIVATE(hook, NULL); /* paranoid */
595 sc->debughook = hook;
599 * Check for raw mode hook.
601 if(strcmp(name, NG_ING_HOOK_RAW) == 0)
603 NG_HOOK_SET_PRIVATE(hook, sc);
611 /*---------------------------------------------------------------------------*
612 * Get a netgraph control message.
613 * Check it is one we understand. If needed, send a response.
614 * We could save the address for an async action later, but don't here.
615 * Always free the message.
616 * The response should be in a malloc'd region that the caller can 'free'.
617 * A response is not required.
618 *---------------------------------------------------------------------------*/
620 ng_ing_rcvmsg(node_p node, item_p item, hook_p lasthook)
622 struct ing_softc *sc = NG_NODE_PRIVATE(node);
624 struct ng_mesg *resp = NULL;
628 NGI_GET_MSG(item, msg);
630 if(msg->header.typecookie == NGM_GENERIC_COOKIE)
632 switch(msg->header.cmd)
634 case NGM_TEXT_STATUS:
640 NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT);
647 arg = (char *) resp->data;
665 pos = sprintf(arg, "state = %s (%d)\n", p, sc->sc_state);
667 pos += sprintf(arg + pos, "%d bytes in, %d bytes out\n", sc->sc_inb, sc->sc_outb);
669 pos += sprintf(arg + pos, "%d pkts in, %d pkts out\n", sc->sc_inpkt, sc->sc_outpkt);
671 resp->header.arglen = pos + 1;
680 else if(msg->header.typecookie == NGM_ING_COOKIE)
682 switch (msg->header.cmd)
684 case NGM_ING_GET_STATUS:
686 struct ngingstat *stats;
688 NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
696 stats = (struct ngingstat *) resp->data;
697 stats->packets_in = sc->packets_in;
698 stats->packets_out = sc->packets_out;
702 case NGM_ING_SET_FLAG:
703 if (msg->header.arglen != sizeof(u_int32_t))
708 sc->flags = *((u_int32_t *) msg->data);
712 error = EINVAL; /* unknown command */
718 error = EINVAL; /* unknown cookie type */
721 /* Take care of synchronous response, if any */
722 NG_RESPOND_MSG(error, node, item, resp);
723 /* Free the message and return */
728 /*---------------------------------------------------------------------------*
729 * get data from another node and transmit it out on a B-channel
730 *---------------------------------------------------------------------------*/
732 ng_ing_rcvdata(hook_p hook, item_p item)
734 struct ing_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
735 struct ifqueue *xmitq_p;
738 struct ng_tag_prio *ptag;
743 if(NG_HOOK_PRIVATE(hook) == NULL)
749 if(sc->sc_state == ST_IDLE || sc->sc_state == ST_DIALING)
751 i4b_l4_dialout(BDRV_ING, sc->sc_unit);
752 sc->sc_state = ST_DIALING;
758 * Now queue the data for when it can be sent
760 if ((ptag = (struct ng_tag_prio *)m_tag_locate(m, NGM_GENERIC_COOKIE,
761 NG_TAG_PRIO, NULL)) != NULL && (ptag->priority > NG_PRIO_CUTOFF) )
762 xmitq_p = (&sc->xmitq_hipri);
764 xmitq_p = (&sc->xmitq);
769 if (_IF_QFULL(xmitq_p))
778 _IF_ENQUEUE(xmitq_p, m);
781 ing_tx_queue_empty(sc->sc_unit);
787 /*---------------------------------------------------------------------------*
788 * Do local shutdown processing..
789 * If we are a persistant device, we might refuse to go away, and
790 * we'd only remove our links and reset ourself.
791 *---------------------------------------------------------------------------*/
793 ng_ing_shutdown(node_p node)
795 struct ing_softc *sc = NG_NODE_PRIVATE(node);
800 sc->packets_in = 0; /* reset stats */
803 if ((ret = ng_make_node_common(&typestruct, &sc->node)))
805 printf("ing: ng_make_node_common, ret = %d\n!", ret);
808 /* name the netgraph node */
809 sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit);
810 if((ret = ng_name_node(sc->node, sc->nodename)))
812 printf("ing: ng_name node, ret = %d\n!", ret);
813 NG_NODE_UNREF(sc->node);
817 NG_NODE_SET_PRIVATE(sc->node, sc);
822 /*---------------------------------------------------------------------------*
823 * This is called once we've already connected a new hook to the other node.
824 *---------------------------------------------------------------------------*/
826 ng_ing_connect(hook_p hook)
828 /* probably not at splnet, force outward queueing */
829 NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
836 * For this type, removal of the last link destroys the node
839 ng_ing_disconnect(hook_p hook)
841 struct ing_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
844 if(NG_HOOK_PRIVATE(hook))
851 sc->debughook = NULL;
856 /*===========================================================================*/