freebsd-dev/sys/i4b/driver/i4b_ing.c
Julian Elischer 069154d55f Rewrite of netgraph to start getting ready for SMP.
This version is functional and is aproaching solid..
notice I said APROACHING. There are many node types I cannot test
I have tested: echo hole ppp socket vjc iface tee bpf async tty
The rest compile and "Look" right.  More changes to follow.
DEBUGGING is enabled in this code to help if people have problems.
2001-01-06 00:46:47 +00:00

883 lines
22 KiB
C

/*
* Copyright (c) 1999, 2000 Hellmuth Michaelis. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*---------------------------------------------------------------------------
*
* i4b_ing.c - isdn4bsd B-channel to netgraph driver
* -------------------------------------------------
*
* $Id: i4b_ing.c,v 1.10 2000/04/27 11:35:00 hm Exp $
*
* $FreeBSD$
*
* last edit-date: [Thu Nov 9 11:29:12 2000]
*
*---------------------------------------------------------------------------*/
#include "i4bing.h"
#if NI4BING > 0
#include "opt_i4b.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <net/if.h>
#include <netgraph/ng_message.h>
#include <netgraph/ng_parse.h>
#include <netgraph/netgraph.h>
#include <machine/i4b_ioctl.h>
#include <machine/i4b_debug.h>
#include <i4b/include/i4b_global.h>
#include <i4b/include/i4b_l3l4.h>
#include <i4b/layer4/i4b_l4.h>
#define I4BINGACCT 1 /* enable accounting messages */
#define I4BINGACCTINTVL 2 /* accounting msg interval in secs */
#define I4BINGMAXQLEN 50 /* max queue length */
/* initialized by L4 */
static drvr_link_t ing_drvr_linktab[NI4BING];
static isdn_link_t *isdn_linktab[NI4BING];
struct ing_softc {
int sc_unit; /* unit number */
int sc_state; /* state of the interface */
call_desc_t *sc_cdp; /* ptr to call descriptor */
int sc_updown; /* soft state of interface */
struct ifqueue sc_fastq; /* interactive traffic */
int sc_dialresp; /* dialresponse */
int sc_lastdialresp;/* last dialresponse */
#if I4BINGACCT
struct callout_handle sc_callout;
int sc_iinb; /* isdn driver # of inbytes */
int sc_ioutb; /* isdn driver # of outbytes */
int sc_inb; /* # of bytes rx'd */
int sc_outb; /* # of bytes tx'd */
int sc_linb; /* last # of bytes rx'd */
int sc_loutb; /* last # of bytes tx'd */
int sc_fn; /* flag, first null acct */
#endif
int sc_inpkt; /* incoming packets */
int sc_outpkt; /* outgoing packets */
struct ifqueue xmitq_hipri; /* hi-priority transmit queue */
struct ifqueue xmitq; /* transmit queue */
node_p node; /* back pointer to node */
char nodename[NG_NODELEN + 1]; /* store our node name */
hook_p debughook;
hook_p hook;
u_int packets_in; /* packets in from downstream */
u_int packets_out; /* packets out towards downstream */
u_int32_t flags;
} ing_softc[NI4BING];
enum ing_states {
ST_IDLE, /* initialized, ready, idle */
ST_DIALING, /* dialling out to remote */
ST_CONNECTED /* connected to remote */
};
static void i4bingattach(void *);
PSEUDO_SET(i4bingattach, i4b_ing);
static void ing_init_linktab(int unit);
static void ing_tx_queue_empty(int unit);
/* ========= NETGRAPH ============= */
#define NG_ING_NODE_TYPE "i4bing" /* node type name */
#define NGM_ING_COOKIE 947513046 /* node type cookie */
/* Hook names */
#define NG_ING_HOOK_DEBUG "debug"
#define NG_ING_HOOK_RAW "rawdata"
/* Netgraph commands understood by this node type */
enum {
NGM_ING_SET_FLAG = 1,
NGM_ING_GET_STATUS,
};
/* This structure is returned by the NGM_ING_GET_STATUS command */
struct ngingstat {
u_int packets_in; /* packets in from downstream */
u_int packets_out; /* packets out towards downstream */
};
/*
* This is used to define the 'parse type' for a struct ngingstat, which
* is bascially a description of how to convert a binary struct ngingstat
* to an ASCII string and back. See ng_parse.h for more info.
*
* This needs to be kept in sync with the above structure definition
*/
#define NG_ING_STATS_TYPE_INFO { \
{ \
{ "packets_in", &ng_parse_int32_type }, \
{ "packets_out", &ng_parse_int32_type }, \
{ NULL }, \
} \
}
/*
* This section contains the netgraph method declarations for the
* sample node. These methods define the netgraph 'type'.
*/
static ng_constructor_t ng_ing_constructor;
static ng_rcvmsg_t ng_ing_rcvmsg;
static ng_shutdown_t ng_ing_shutdown;
static ng_newhook_t ng_ing_newhook;
static ng_connect_t ng_ing_connect;
static ng_rcvdata_t ng_ing_rcvdata;
static ng_disconnect_t ng_ing_disconnect;
/* Parse type for struct ngingstat */
static const struct
ng_parse_struct_info ng_ing_stat_type_info = NG_ING_STATS_TYPE_INFO;
static const struct ng_parse_type ng_ing_stat_type = {
&ng_parse_struct_type,
&ng_ing_stat_type_info
};
/* List of commands and how to convert arguments to/from ASCII */
static const struct ng_cmdlist ng_ing_cmdlist[] = {
{
NGM_ING_COOKIE,
NGM_ING_GET_STATUS,
"getstatus",
NULL,
&ng_ing_stat_type,
},
{
NGM_ING_COOKIE,
NGM_ING_SET_FLAG,
"setflag",
&ng_parse_int32_type,
NULL
},
{ 0 }
};
/* Netgraph node type descriptor */
static struct ng_type typestruct = {
NG_ABI_VERSION,
NG_ING_NODE_TYPE,
NULL,
ng_ing_constructor,
ng_ing_rcvmsg,
ng_ing_shutdown,
ng_ing_newhook,
NULL,
ng_ing_connect,
ng_ing_rcvdata,
ng_ing_disconnect,
ng_ing_cmdlist
};
NETGRAPH_INIT_ORDERED(ing, &typestruct, SI_SUB_DRIVERS, SI_ORDER_ANY);
/*===========================================================================*
* DEVICE DRIVER ROUTINES
*===========================================================================*/
/*---------------------------------------------------------------------------*
* interface attach routine at kernel boot time
*---------------------------------------------------------------------------*/
static void
i4bingattach(void *dummy)
{
struct ing_softc *sc = ing_softc;
int i;
int ret;
printf("i4bing: %d i4b NetGraph ISDN B-channel device(s) attached\n", NI4BING);
for(i=0; i < NI4BING; sc++, i++)
{
sc->sc_unit = i;
ing_init_linktab(i);
NDBGL4(L4_DIALST, "setting dial state to ST_IDLE");
sc->sc_state = ST_IDLE;
sc->sc_fastq.ifq_maxlen = I4BINGMAXQLEN;
mtx_init(&sc->sc_fastq.ifq_mtx, "i4b_ing_fastq", MTX_DEF);
#if I4BINGACCT
callout_handle_init(&sc->sc_callout);
sc->sc_iinb = 0;
sc->sc_ioutb = 0;
sc->sc_inb = 0;
sc->sc_outb = 0;
sc->sc_linb = 0;
sc->sc_loutb = 0;
sc->sc_fn = 1;
#endif
sc->sc_inpkt = 0;
sc->sc_outpkt = 0;
sc->sc_updown = SOFT_ENA; /* soft enabled */
sc->sc_dialresp = DSTAT_NONE; /* no response */
sc->sc_lastdialresp = DSTAT_NONE;
/* setup a netgraph node */
if ((ret = ng_make_node_common(&typestruct, &sc->node)))
{
printf("ing: ng_make_node_common, ret = %d\n!", ret);
}
/* name the netgraph node */
sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit);
if((ret = ng_name_node(sc->node, sc->nodename)))
{
printf("ing: ng_name node, ret = %d\n!", ret);
ng_unref(sc->node);
break;
}
sc->node->private = sc;
sc->xmitq.ifq_maxlen = IFQ_MAXLEN;
sc->xmitq_hipri.ifq_maxlen = IFQ_MAXLEN;
mtx_init(&sc->xmitq.ifq_mtx, "i4b_ing_xmitq", MTX_DEF);
mtx_init(&sc->xmitq_hipri.ifq_mtx, "i4b_ing_hipri", MTX_DEF);
}
}
#ifdef I4BINGACCT
/*---------------------------------------------------------------------------*
* accounting timeout routine
*---------------------------------------------------------------------------*/
static void
ing_timeout(struct ing_softc *sc)
{
bchan_statistics_t bs;
int unit = sc->sc_unit;
/* get # of bytes in and out from the HSCX driver */
(*isdn_linktab[unit]->bch_stat)
(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
sc->sc_ioutb += bs.outbytes;
sc->sc_iinb += bs.inbytes;
if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn)
{
int ri = (sc->sc_iinb - sc->sc_linb)/I4BINGACCTINTVL;
int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BINGACCTINTVL;
if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
sc->sc_fn = 0;
else
sc->sc_fn = 1;
sc->sc_linb = sc->sc_iinb;
sc->sc_loutb = sc->sc_ioutb;
i4b_l4_accounting(BDRV_ING, unit, ACCT_DURING,
sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_ioutb, sc->sc_iinb);
}
sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout,
(void *)sc, I4BINGACCTINTVL*hz);
}
#endif /* I4BINGACCT */
#if 0
/*---------------------------------------------------------------------------*
* clear the interface's send queues
*---------------------------------------------------------------------------*/
static void
ingclearqueue(struct ifqueue *iq)
{
int x;
x = splimp();
IF_DRAIN(iq);
splx(x);
}
#endif
/*===========================================================================*
* ISDN INTERFACE ROUTINES
*===========================================================================*/
/*---------------------------------------------------------------------------*
* this routine is called from L4 handler at connect time
*---------------------------------------------------------------------------*/
static void
ing_connect(int unit, void *cdp)
{
struct ing_softc *sc = &ing_softc[unit];
int s;
sc->sc_cdp = (call_desc_t *)cdp;
s = SPLI4B();
NDBGL4(L4_DIALST, "ing%d: setting dial state to ST_CONNECTED", unit);
sc->sc_dialresp = DSTAT_NONE;
sc->sc_lastdialresp = DSTAT_NONE;
#if I4BINGACCT
sc->sc_iinb = 0;
sc->sc_ioutb = 0;
sc->sc_inb = 0;
sc->sc_outb = 0;
sc->sc_linb = 0;
sc->sc_loutb = 0;
sc->sc_callout = timeout((TIMEOUT_FUNC_T)ing_timeout,
(void *)sc, I4BINGACCTINTVL*hz);
#endif
sc->sc_state = ST_CONNECTED;
splx(s);
}
/*---------------------------------------------------------------------------*
* this routine is called from L4 handler at disconnect time
*---------------------------------------------------------------------------*/
static void
ing_disconnect(int unit, void *cdp)
{
call_desc_t *cd = (call_desc_t *)cdp;
struct ing_softc *sc = &ing_softc[unit];
/* new stuff to check that the active channel is being closed */
if (cd != sc->sc_cdp)
{
NDBGL4(L4_INGDBG, "ing%d: channel %d not active",
cd->driver_unit, cd->channelid);
return;
}
#if I4BINGACCT
untimeout((TIMEOUT_FUNC_T)ing_timeout,
(void *)sc, sc->sc_callout);
#endif
i4b_l4_accounting(BDRV_ING, cd->driver_unit, ACCT_FINAL,
sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb);
sc->sc_cdp = (call_desc_t *)0;
NDBGL4(L4_DIALST, "setting dial state to ST_IDLE");
sc->sc_dialresp = DSTAT_NONE;
sc->sc_lastdialresp = DSTAT_NONE;
sc->sc_state = ST_IDLE;
}
/*---------------------------------------------------------------------------*
* this routine is used to give a feedback from userland daemon
* in case of dial problems
*---------------------------------------------------------------------------*/
static void
ing_dialresponse(int unit, int status, cause_t cause)
{
struct ing_softc *sc = &ing_softc[unit];
sc->sc_dialresp = status;
NDBGL4(L4_INGDBG, "ing%d: last=%d, this=%d",
unit, sc->sc_lastdialresp, sc->sc_dialresp);
if(status != DSTAT_NONE)
{
NDBGL4(L4_INGDBG, "ing%d: clearing queues", unit);
/* ingclearqueues(sc); */
}
}
/*---------------------------------------------------------------------------*
* interface soft up/down
*---------------------------------------------------------------------------*/
static void
ing_updown(int unit, int updown)
{
struct ing_softc *sc = &ing_softc[unit];
sc->sc_updown = updown;
}
/*---------------------------------------------------------------------------*
* this routine is called from the HSCX interrupt handler
* when a new frame (mbuf) has been received and was put on
* the rx queue. It is assumed that this routines runs at
* pri level splimp() ! Keep it short !
*---------------------------------------------------------------------------*/
static void
ing_rx_data_rdy(int unit)
{
register struct ing_softc *sc = &ing_softc[unit];
register struct mbuf *m;
int error;
if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
return;
#if I4BINGACCT
sc->sc_inb += m->m_pkthdr.len;
#endif
m->m_pkthdr.rcvif = NULL;
sc->sc_inpkt++;
NG_SEND_DATA_ONLY(error, sc->hook, m);
}
/*---------------------------------------------------------------------------*
* this routine is called from the HSCX interrupt handler
* when the last frame has been sent out and there is no
* further frame (mbuf) in the tx queue.
*---------------------------------------------------------------------------*/
static void
ing_tx_queue_empty(int unit)
{
register struct ing_softc *sc = &ing_softc[unit];
register struct mbuf *m;
int x = 0;
if(sc->sc_state != ST_CONNECTED)
return;
for(;;)
{
IF_DEQUEUE(&sc->xmitq_hipri, m);
if(m == NULL)
{
IF_DEQUEUE(&sc->xmitq, m);
if(m == NULL)
break;
}
#if I4BINGACCT
sc->sc_outb += m->m_pkthdr.len;
#endif
x = 1;
if(! IF_HANDOFF(isdn_linktab[unit]->tx_queue, m, NULL))
{
NDBGL4(L4_INGDBG, "ing%d: tx queue full!", unit);
}
}
if(x)
(*isdn_linktab[unit]->bch_tx_start)(isdn_linktab[unit]->unit, isdn_linktab[unit]->channel);
}
/*---------------------------------------------------------------------------*
* this routine is called from the HSCX interrupt handler
* each time a packet is received or transmitted. It should
* be used to implement an activity timeout mechanism.
*---------------------------------------------------------------------------*/
static void
ing_activity(int unit, int rxtx)
{
ing_softc[unit].sc_cdp->last_active_time = SECOND;
}
/*---------------------------------------------------------------------------*
* return this drivers linktab address
*---------------------------------------------------------------------------*/
drvr_link_t *
ing_ret_linktab(int unit)
{
return(&ing_drvr_linktab[unit]);
}
/*---------------------------------------------------------------------------*
* setup the isdn_linktab for this driver
*---------------------------------------------------------------------------*/
void
ing_set_linktab(int unit, isdn_link_t *ilt)
{
isdn_linktab[unit] = ilt;
}
/*---------------------------------------------------------------------------*
* initialize this drivers linktab
*---------------------------------------------------------------------------*/
static void
ing_init_linktab(int unit)
{
ing_drvr_linktab[unit].unit = unit;
ing_drvr_linktab[unit].bch_rx_data_ready = ing_rx_data_rdy;
ing_drvr_linktab[unit].bch_tx_queue_empty = ing_tx_queue_empty;
ing_drvr_linktab[unit].bch_activity = ing_activity;
ing_drvr_linktab[unit].line_connected = ing_connect;
ing_drvr_linktab[unit].line_disconnected = ing_disconnect;
ing_drvr_linktab[unit].dial_response = ing_dialresponse;
ing_drvr_linktab[unit].updown_ind = ing_updown;
}
/*===========================================================================*
* NETGRAPH INTERFACE ROUTINES
*===========================================================================*/
/*---------------------------------------------------------------------------*
* It is not possible or allowable to create a node of this type.
* If the hardware exists, it will already have created it.
*---------------------------------------------------------------------------*/
static int
ng_ing_constructor(node_p node)
{
return(EINVAL);
}
/*---------------------------------------------------------------------------*
* Give our ok for a hook to be added...
* Add the hook's private info to the hook structure.
*---------------------------------------------------------------------------*/
static int
ng_ing_newhook(node_p node, hook_p hook, const char *name)
{
struct ing_softc *sc = node->private;
/*
* check if it's our friend the debug hook
*/
if(strcmp(name, NG_ING_HOOK_DEBUG) == 0)
{
hook->private = NULL; /* paranoid */
sc->debughook = hook;
return (0);
}
/*
* Check for raw mode hook.
*/
if(strcmp(name, NG_ING_HOOK_RAW) == 0)
{
hook->private = sc;
sc->hook = hook;
return (0);
}
return (EINVAL);
}
/*---------------------------------------------------------------------------*
* Get a netgraph control message.
* Check it is one we understand. If needed, send a response.
* We could save the address for an async action later, but don't here.
* Always free the message.
* The response should be in a malloc'd region that the caller can 'free'.
* A response is not required.
*---------------------------------------------------------------------------*/
#if defined(__FreeBSD_version) && __FreeBSD_version >= 500000
static int
ng_ing_rcvmsg(node_p node, item_p item, hook_p lasthook)
#else
static int
ng_ing_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
struct ng_mesg **rptr)
#endif
{
struct ing_softc *sc = node->private;
struct ng_mesg *resp = NULL;
int error = 0;
#if defined(__FreeBSD_version) && __FreeBSD_version >= 500000
struct ng_mesg *msg;
NGI_GET_MSG(item, msg);
#endif
if(msg->header.typecookie == NGM_GENERIC_COOKIE)
{
switch(msg->header.cmd)
{
case NGM_TEXT_STATUS:
{
char *arg;
char *p;
int pos = 0;
NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT);
if (resp == NULL)
{
error = ENOMEM;
break;
}
arg = (char *) resp->data;
switch(sc->sc_state)
{
case ST_IDLE:
p = "idle";
break;
case ST_DIALING:
p = "dialing";
break;
case ST_CONNECTED:
p = "connected";
break;
default:
p = "???";
break;
}
pos = sprintf(arg, "state = %s (%d)\n", p, sc->sc_state);
#if I4BINGACCT
pos += sprintf(arg + pos, "%d bytes in, %d bytes out\n", sc->sc_inb, sc->sc_outb);
#endif
pos += sprintf(arg + pos, "%d pkts in, %d pkts out\n", sc->sc_inpkt, sc->sc_outpkt);
resp->header.arglen = pos + 1;
break;
}
default:
error = EINVAL;
break;
}
}
else if(msg->header.typecookie == NGM_ING_COOKIE)
{
switch (msg->header.cmd)
{
case NGM_ING_GET_STATUS:
{
struct ngingstat *stats;
NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
if (!resp)
{
error = ENOMEM;
break;
}
stats = (struct ngingstat *) resp->data;
stats->packets_in = sc->packets_in;
stats->packets_out = sc->packets_out;
break;
}
case NGM_ING_SET_FLAG:
if (msg->header.arglen != sizeof(u_int32_t))
{
error = EINVAL;
break;
}
sc->flags = *((u_int32_t *) msg->data);
break;
default:
error = EINVAL; /* unknown command */
break;
}
}
else
{
error = EINVAL; /* unknown cookie type */
}
/* Take care of synchronous response, if any */
NG_RESPOND_MSG(error, node, item, resp);
/* Free the message and return */
NG_FREE_MSG(msg);
return(error);
}
/*---------------------------------------------------------------------------*
* get data from another node and transmit it out on a B-channel
*---------------------------------------------------------------------------*/
#if defined(__FreeBSD_version) && __FreeBSD_version >= 500000
static int
ng_ing_rcvdata(hook_p hook, item_p item)
#else
static int
ng_ing_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
#endif
{
struct ing_softc *sc = hook->node->private;
struct ifqueue *xmitq_p;
int s;
#if defined(__FreeBSD_version) && __FreeBSD_version >= 500000
struct mbuf *m;
meta_p meta;
NGI_GET_M(item, m);
NGI_GET_META(item, meta);
NG_FREE_ITEM(item);
#endif
if(hook->private == NULL)
{
NG_FREE_M(m);
NG_FREE_META(meta);
return(ENETDOWN);
}
if(sc->sc_state == ST_IDLE || sc->sc_state == ST_DIALING)
{
i4b_l4_dialout(BDRV_ING, sc->sc_unit);
sc->sc_state = ST_DIALING;
}
sc->sc_outpkt++;
/*
* Now queue the data for when it can be sent
*/
if (meta && meta->priority > 0)
{
xmitq_p = (&sc->xmitq_hipri);
}
else
{
xmitq_p = (&sc->xmitq);
}
s = splimp();
IF_LOCK(xmitq_p);
if (_IF_QFULL(xmitq_p))
{
_IF_DROP(xmitq_p);
IF_UNLOCK(xmitq_p);
splx(s);
NG_FREE_M(m);
NG_FREE_META(meta);
return(ENOBUFS);
}
_IF_ENQUEUE(xmitq_p, m);
IF_UNLOCK(xmitq_p);
ing_tx_queue_empty(sc->sc_unit);
splx(s);
return (0);
}
/*---------------------------------------------------------------------------*
* Do local shutdown processing..
* If we are a persistant device, we might refuse to go away, and
* we'd only remove our links and reset ourself.
*---------------------------------------------------------------------------*/
static int
ng_ing_shutdown(node_p node)
{
struct ing_softc *sc = node->private;
int ret;
node->flags |= NG_INVALID;
ng_unref(node);
sc->packets_in = 0; /* reset stats */
sc->packets_out = 0;
if ((ret = ng_make_node_common(&typestruct, &sc->node)))
{
printf("ing: ng_make_node_common, ret = %d\n!", ret);
}
/* name the netgraph node */
sprintf(sc->nodename, "%s%d", NG_ING_NODE_TYPE, sc->sc_unit);
if((ret = ng_name_node(sc->node, sc->nodename)))
{
printf("ing: ng_name node, ret = %d\n!", ret);
ng_unref(sc->node);
return (0);
}
sc->node->private = sc;
return (0);
}
/*---------------------------------------------------------------------------*
* This is called once we've already connected a new hook to the other node.
*---------------------------------------------------------------------------*/
static int
ng_ing_connect(hook_p hook)
{
/* probably not at splnet, force outward queueing */
hook->peer->flags |= HK_QUEUE;
return (0);
}
/*
* Dook disconnection
*
* For this type, removal of the last link destroys the node
*/
static int
ng_ing_disconnect(hook_p hook)
{
struct ing_softc *sc = hook->node->private;
int s;
if(hook->private)
{
s = splimp();
splx(s);
}
else
{
sc->debughook = NULL;
}
return (0);
}
/*===========================================================================*/
#endif /* NI4BING > 0 */